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

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : /* Copyright © 2019 Oracle and/or its affiliates. */
       4                 :            : 
       5                 :            : /* Generally speaking, the pstore contains a small number of files
       6                 :            :  * that in turn contain a small amount of data.  */
       7                 :            : #include <errno.h>
       8                 :            : #include <stdio.h>
       9                 :            : #include <stdio_ext.h>
      10                 :            : #include <sys/prctl.h>
      11                 :            : #include <sys/xattr.h>
      12                 :            : #include <unistd.h>
      13                 :            : 
      14                 :            : #include "sd-daemon.h"
      15                 :            : #include "sd-journal.h"
      16                 :            : #include "sd-login.h"
      17                 :            : #include "sd-messages.h"
      18                 :            : 
      19                 :            : #include "acl-util.h"
      20                 :            : #include "alloc-util.h"
      21                 :            : #include "capability-util.h"
      22                 :            : #include "cgroup-util.h"
      23                 :            : #include "compress.h"
      24                 :            : #include "conf-parser.h"
      25                 :            : #include "copy.h"
      26                 :            : #include "dirent-util.h"
      27                 :            : #include "escape.h"
      28                 :            : #include "fd-util.h"
      29                 :            : #include "fileio.h"
      30                 :            : #include "fs-util.h"
      31                 :            : #include "io-util.h"
      32                 :            : #include "journal-importer.h"
      33                 :            : #include "log.h"
      34                 :            : #include "macro.h"
      35                 :            : #include "main-func.h"
      36                 :            : #include "missing.h"
      37                 :            : #include "mkdir.h"
      38                 :            : #include "parse-util.h"
      39                 :            : #include "process-util.h"
      40                 :            : #include "signal-util.h"
      41                 :            : #include "socket-util.h"
      42                 :            : #include "special.h"
      43                 :            : #include "sort-util.h"
      44                 :            : #include "string-table.h"
      45                 :            : #include "string-util.h"
      46                 :            : #include "strv.h"
      47                 :            : #include "tmpfile-util.h"
      48                 :            : #include "user-util.h"
      49                 :            : #include "util.h"
      50                 :            : 
      51                 :            : /* Command line argument handling */
      52                 :            : typedef enum PStoreStorage {
      53                 :            :         PSTORE_STORAGE_NONE,
      54                 :            :         PSTORE_STORAGE_EXTERNAL,
      55                 :            :         PSTORE_STORAGE_JOURNAL,
      56                 :            :         _PSTORE_STORAGE_MAX,
      57                 :            :         _PSTORE_STORAGE_INVALID = -1
      58                 :            : } PStoreStorage;
      59                 :            : 
      60                 :            : static const char* const pstore_storage_table[_PSTORE_STORAGE_MAX] = {
      61                 :            :         [PSTORE_STORAGE_NONE] = "none",
      62                 :            :         [PSTORE_STORAGE_EXTERNAL] = "external",
      63                 :            :         [PSTORE_STORAGE_JOURNAL] = "journal",
      64                 :            : };
      65                 :            : 
      66   [ #  #  #  # ]:          0 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP(pstore_storage, PStoreStorage);
      67   [ #  #  #  #  :          0 : static DEFINE_CONFIG_PARSE_ENUM(config_parse_pstore_storage, pstore_storage, PStoreStorage, "Failed to parse storage setting");
          #  #  #  #  #  
                #  #  # ]
      68                 :            : 
      69                 :            : static PStoreStorage arg_storage = PSTORE_STORAGE_EXTERNAL;
      70                 :            : 
      71                 :            : static bool arg_unlink = true;
      72                 :            : static const char *arg_sourcedir = "/sys/fs/pstore";
      73                 :            : static const char *arg_archivedir = "/var/lib/systemd/pstore";
      74                 :            : 
      75                 :          0 : static int parse_config(void) {
      76                 :            :         static const ConfigTableItem items[] = {
      77                 :            :                 { "PStore", "Unlink",  config_parse_bool,           0, &arg_unlink },
      78                 :            :                 { "PStore", "Storage", config_parse_pstore_storage, 0, &arg_storage },
      79                 :            :                 {}
      80                 :            :         };
      81                 :            : 
      82                 :          0 :         return config_parse_many_nulstr(PKGSYSCONFDIR "/pstore.conf",
      83                 :            :                                         CONF_PATHS_NULSTR("systemd/pstore.conf.d"),
      84                 :            :                                         "PStore\0",
      85                 :            :                                         config_item_table_lookup, items,
      86                 :            :                                         CONFIG_PARSE_WARN, NULL);
      87                 :            : }
      88                 :            : 
      89                 :            : /* File list handling - PStoreEntry is the struct and
      90                 :            :  * and PStoreEntry is the type that contains all info
      91                 :            :  * about a pstore entry.  */
      92                 :            : typedef struct PStoreEntry {
      93                 :            :         struct dirent dirent;
      94                 :            :         bool is_binary;
      95                 :            :         bool handled;
      96                 :            :         char *content;
      97                 :            :         size_t content_size;
      98                 :            : } PStoreEntry;
      99                 :            : 
     100                 :            : typedef struct PStoreList {
     101                 :            :         PStoreEntry *entries;
     102                 :            :         size_t n_entries;
     103                 :            :         size_t n_entries_allocated;
     104                 :            : } PStoreList;
     105                 :            : 
     106                 :          0 : static void pstore_entries_reset(PStoreList *list) {
     107         [ #  # ]:          0 :         for (size_t i = 0; i < list->n_entries; i++)
     108                 :          0 :                 free(list->entries[i].content);
     109                 :          0 :         free(list->entries);
     110                 :          0 :         list->n_entries = 0;
     111                 :          0 : }
     112                 :            : 
     113                 :          0 : static int compare_pstore_entries(const void *_a, const void *_b) {
     114                 :          0 :         PStoreEntry *a = (PStoreEntry *)_a, *b = (PStoreEntry *)_b;
     115                 :          0 :         return strcmp(a->dirent.d_name, b->dirent.d_name);
     116                 :            : }
     117                 :            : 
     118                 :          0 : static int move_file(PStoreEntry *pe, const char *subdir) {
     119                 :          0 :         _cleanup_free_ char *ifd_path = NULL, *ofd_path = NULL;
     120                 :            :         const char *suffix, *message;
     121                 :            :         struct iovec iovec[2];
     122                 :          0 :         int n_iovec = 0, r;
     123                 :            : 
     124         [ #  # ]:          0 :         if (pe->handled)
     125                 :          0 :                 return 0;
     126                 :            : 
     127                 :          0 :         ifd_path = path_join(arg_sourcedir, pe->dirent.d_name);
     128         [ #  # ]:          0 :         if (!ifd_path)
     129                 :          0 :                 return log_oom();
     130                 :            : 
     131                 :          0 :         ofd_path = path_join(arg_archivedir, subdir, pe->dirent.d_name);
     132         [ #  # ]:          0 :         if (!ofd_path)
     133                 :          0 :                 return log_oom();
     134                 :            : 
     135                 :            :         /* Always log to the journal */
     136   [ #  #  #  #  :          0 :         suffix = arg_storage == PSTORE_STORAGE_EXTERNAL ? strjoina(" moved to ", ofd_path) : (char *)".";
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     137   [ #  #  #  #  :          0 :         message = strjoina("MESSAGE=PStore ", pe->dirent.d_name, suffix);
          #  #  #  #  #  
                #  #  # ]
     138                 :          0 :         iovec[n_iovec++] = IOVEC_MAKE_STRING(message);
     139                 :            : 
     140         [ #  # ]:          0 :         if (pe->content_size > 0) {
     141         [ #  # ]:          0 :                 _cleanup_free_ void *field = NULL;
     142                 :            :                 size_t field_size;
     143                 :            : 
     144                 :          0 :                 field_size = strlen("FILE=") + pe->content_size;
     145                 :          0 :                 field = malloc(field_size);
     146         [ #  # ]:          0 :                 if (!field)
     147                 :          0 :                         return log_oom();
     148                 :          0 :                 memcpy(stpcpy(field, "FILE="), pe->content, pe->content_size);
     149                 :          0 :                 iovec[n_iovec++] = IOVEC_MAKE(field, field_size);
     150                 :            :         }
     151                 :            : 
     152                 :          0 :         r = sd_journal_sendv(iovec, n_iovec);
     153         [ #  # ]:          0 :         if (r < 0)
     154         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to log pstore entry: %m");
     155                 :            : 
     156         [ #  # ]:          0 :         if (arg_storage == PSTORE_STORAGE_EXTERNAL) {
     157                 :            :                 /* Move file from pstore to external storage */
     158                 :          0 :                 r = mkdir_parents(ofd_path, 0755);
     159         [ #  # ]:          0 :                 if (r < 0)
     160         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to create directoy %s: %m", ofd_path);
     161                 :          0 :                 r = copy_file_atomic(ifd_path, ofd_path, 0600, 0, 0, COPY_REPLACE);
     162         [ #  # ]:          0 :                 if (r < 0)
     163         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to copy_file_atomic: %s to %s", ifd_path, ofd_path);
     164                 :            :         }
     165                 :            : 
     166                 :            :         /* If file copied properly, remove it from pstore */
     167         [ #  # ]:          0 :         if (arg_unlink)
     168                 :          0 :                 (void) unlink(ifd_path);
     169                 :            : 
     170                 :          0 :         pe->handled = true;
     171                 :            : 
     172                 :          0 :         return 0;
     173                 :            : }
     174                 :            : 
     175                 :          0 : static int write_dmesg(const char *dmesg, size_t size, const char *id) {
     176                 :          0 :         _cleanup_(unlink_and_freep) char *tmp_path = NULL;
     177                 :          0 :         _cleanup_free_ char *ofd_path = NULL;
     178                 :          0 :         _cleanup_close_ int ofd = -1;
     179                 :            :         ssize_t wr;
     180                 :            :         int r;
     181                 :            : 
     182   [ #  #  #  # ]:          0 :         if (isempty(dmesg) || size == 0)
     183                 :          0 :                 return 0;
     184                 :            : 
     185                 :          0 :         ofd_path = path_join(arg_archivedir, id, "dmesg.txt");
     186         [ #  # ]:          0 :         if (!ofd_path)
     187                 :          0 :                 return log_oom();
     188                 :            : 
     189                 :          0 :         ofd = open_tmpfile_linkable(ofd_path, O_CLOEXEC|O_CREAT|O_TRUNC|O_WRONLY, &tmp_path);
     190         [ #  # ]:          0 :         if (ofd < 0)
     191         [ #  # ]:          0 :                 return log_error_errno(ofd, "Failed to open temporary file %s: %m", ofd_path);
     192                 :          0 :         wr = write(ofd, dmesg, size);
     193         [ #  # ]:          0 :         if (wr < 0)
     194         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to store dmesg to %s: %m", ofd_path);
     195         [ #  # ]:          0 :         if (wr != (ssize_t)size)
     196         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to store dmesg to %s. %zu bytes are lost.", ofd_path, size - wr);
     197                 :          0 :         r = link_tmpfile(ofd, tmp_path, ofd_path);
     198         [ #  # ]:          0 :         if (r < 0)
     199         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to write temporary file %s: %m", ofd_path);
     200                 :          0 :         tmp_path = mfree(tmp_path);
     201                 :            : 
     202                 :          0 :         return 0;
     203                 :            : }
     204                 :            : 
     205                 :          0 : static void process_dmesg_files(PStoreList *list) {
     206                 :            :         /* Move files, reconstruct dmesg.txt */
     207   [ #  #  #  # ]:          0 :         _cleanup_free_ char *dmesg = NULL, *dmesg_id = NULL;
     208                 :          0 :         size_t dmesg_size = 0;
     209                 :            :         PStoreEntry *pe;
     210                 :            : 
     211                 :            :         /* Handle each dmesg file: files processed in reverse
     212                 :            :          * order so as to properly reconstruct original dmesg */
     213         [ #  # ]:          0 :         for (size_t n = list->n_entries; n > 0; n--) {
     214                 :          0 :                 bool move_file_and_continue = false;
     215      [ #  #  # ]:          0 :                 _cleanup_free_ char *pe_id = NULL;
     216                 :            :                 char *p;
     217                 :            :                 size_t plen;
     218                 :            : 
     219                 :          0 :                 pe = &list->entries[n-1];
     220                 :            : 
     221         [ #  # ]:          0 :                 if (pe->handled)
     222                 :          0 :                         continue;
     223         [ #  # ]:          0 :                 if (!startswith(pe->dirent.d_name, "dmesg-"))
     224                 :          0 :                         continue;
     225                 :            : 
     226         [ #  # ]:          0 :                 if (endswith(pe->dirent.d_name, ".enc.z")) /* indicates a problem */
     227                 :          0 :                         move_file_and_continue = true;
     228                 :          0 :                 p = strrchr(pe->dirent.d_name, '-');
     229         [ #  # ]:          0 :                 if (!p)
     230                 :          0 :                         move_file_and_continue = true;
     231                 :            : 
     232         [ #  # ]:          0 :                 if (move_file_and_continue) {
     233                 :            :                         /* A dmesg file on which we do NO additional processing */
     234                 :          0 :                         (void) move_file(pe, NULL);
     235                 :          0 :                         continue;
     236                 :            :                 }
     237                 :            : 
     238                 :            :                 /* See if this file is one of a related group of files
     239                 :            :                  * in order to reconstruct dmesg */
     240                 :            : 
     241                 :            :                 /* When dmesg is written into pstore, it is done so in
     242                 :            :                  * small chunks, whatever the exchange buffer size is
     243                 :            :                  * with the underlying pstore backend (ie. EFI may be
     244                 :            :                  * ~2KiB), which means an example pstore with approximately
     245                 :            :                  * 64KB of storage may have up to roughly 32 dmesg files
     246                 :            :                  * that could be related, depending upon the size of the
     247                 :            :                  * original dmesg.
     248                 :            :                  *
     249                 :            :                  * Here we look at the dmesg filename and try to discern
     250                 :            :                  * if files are part of a related group, meaning the same
     251                 :            :                  * original dmesg.
     252                 :            :                  *
     253                 :            :                  * The two known pstore backends are EFI and ERST. These
     254                 :            :                  * backends store data in the Common Platform Error
     255                 :            :                  * Record, CPER, format. The dmesg- filename contains the
     256                 :            :                  * CPER record id, a 64bit number (in decimal notation).
     257                 :            :                  * In Linux, the record id is encoded with two digits for
     258                 :            :                  * the dmesg part (chunk) number and 3 digits for the
     259                 :            :                  * count number. So allowing an additional digit to
     260                 :            :                  * compensate for advancing time, this code ignores the
     261                 :            :                  * last six digits of the filename in determining the
     262                 :            :                  * record id.
     263                 :            :                  *
     264                 :            :                  * For the EFI backend, the record id encodes an id in the
     265                 :            :                  * upper 32 bits, and a timestamp in the lower 32-bits.
     266                 :            :                  * So ignoring the least significant 6 digits has proven
     267                 :            :                  * to generally identify related dmesg entries.  */
     268                 :            : #define PSTORE_FILENAME_IGNORE 6
     269                 :            : 
     270                 :            :                 /* determine common portion of record id */
     271                 :          0 :                 ++p; /* move beyond dmesg- */
     272                 :          0 :                 plen = strlen(p);
     273         [ #  # ]:          0 :                 if (plen > PSTORE_FILENAME_IGNORE) {
     274                 :          0 :                         pe_id = memdup_suffix0(p, plen - PSTORE_FILENAME_IGNORE);
     275         [ #  # ]:          0 :                         if (!pe_id) {
     276                 :          0 :                                 log_oom();
     277                 :          0 :                                 return;
     278                 :            :                         }
     279                 :            :                 } else
     280                 :          0 :                         pe_id = mfree(pe_id);
     281                 :            : 
     282                 :            :                 /* Now move file from pstore to archive storage */
     283                 :          0 :                 move_file(pe, pe_id);
     284                 :            : 
     285                 :            :                 /* If the current record id is NOT the same as the
     286                 :            :                  * previous record id, then start a new dmesg.txt file */
     287   [ #  #  #  #  :          0 :                 if (!pe_id || !dmesg_id || !streq(pe_id, dmesg_id)) {
                   #  # ]
     288                 :            :                         /* Encountered a new dmesg group, close out old one, open new one */
     289         [ #  # ]:          0 :                         if (dmesg) {
     290                 :          0 :                                 (void) write_dmesg(dmesg, dmesg_size, dmesg_id);
     291                 :          0 :                                 dmesg = mfree(dmesg);
     292                 :          0 :                                 dmesg_size = 0;
     293                 :            :                         }
     294                 :            : 
     295                 :            :                         /* now point dmesg_id to storage of pe_id */
     296                 :          0 :                         free_and_replace(dmesg_id, pe_id);
     297                 :            :                 }
     298                 :            : 
     299                 :            :                 /* Reconstruction of dmesg is done as a useful courtesy, do not log errors */
     300                 :          0 :                 dmesg = realloc(dmesg, dmesg_size + strlen(pe->dirent.d_name) + strlen(":\n") + pe->content_size + 1);
     301         [ #  # ]:          0 :                 if (dmesg) {
     302                 :          0 :                         dmesg_size += sprintf(&dmesg[dmesg_size], "%s:\n", pe->dirent.d_name);
     303         [ #  # ]:          0 :                         if (pe->content) {
     304                 :          0 :                                 memcpy(&dmesg[dmesg_size], pe->content, pe->content_size);
     305                 :          0 :                                 dmesg_size += pe->content_size;
     306                 :            :                         }
     307                 :            :                 }
     308                 :            : 
     309                 :          0 :                 pe_id = mfree(pe_id);
     310                 :            :         }
     311         [ #  # ]:          0 :         if (dmesg)
     312                 :          0 :                 (void) write_dmesg(dmesg, dmesg_size, dmesg_id);
     313                 :            : }
     314                 :            : 
     315                 :          0 : static int list_files(PStoreList *list, const char *sourcepath) {
     316                 :          0 :         _cleanup_(closedirp) DIR *dirp = NULL;
     317                 :            :         struct dirent *de;
     318                 :            :         int r;
     319                 :            : 
     320                 :          0 :         dirp = opendir(sourcepath);
     321         [ #  # ]:          0 :         if (!dirp)
     322         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to opendir %s: %m", sourcepath);
     323                 :            : 
     324   [ #  #  #  #  :          0 :         FOREACH_DIRENT(de, dirp, return log_error_errno(errno, "Failed to iterate through %s: %m", sourcepath)) {
             #  #  #  # ]
     325      [ #  #  # ]:          0 :                 _cleanup_free_ char *ifd_path = NULL;
     326                 :            : 
     327                 :          0 :                 ifd_path = path_join(sourcepath, de->d_name);
     328         [ #  # ]:          0 :                 if (!ifd_path)
     329                 :          0 :                         return log_oom();
     330                 :            : 
     331      [ #  #  # ]:          0 :                 _cleanup_free_ char *buf = NULL;
     332                 :            :                 size_t buf_size;
     333                 :            : 
     334                 :            :                 /* Now read contents of pstore file */
     335                 :          0 :                 r = read_full_file(ifd_path, &buf, &buf_size);
     336         [ #  # ]:          0 :                 if (r < 0) {
     337         [ #  # ]:          0 :                         log_warning_errno(r, "Failed to read file %s, skipping: %m", ifd_path);
     338                 :          0 :                         continue;
     339                 :            :                 }
     340                 :            : 
     341         [ #  # ]:          0 :                 if (!GREEDY_REALLOC(list->entries, list->n_entries_allocated, list->n_entries + 1))
     342                 :          0 :                         return log_oom();
     343                 :            : 
     344                 :          0 :                 list->entries[list->n_entries++] = (PStoreEntry) {
     345                 :          0 :                         .dirent = *de,
     346                 :          0 :                         .content = TAKE_PTR(buf),
     347                 :            :                         .content_size = buf_size,
     348                 :            :                         .is_binary = true,
     349                 :            :                         .handled = false,
     350                 :            :                 };
     351                 :            :         }
     352                 :            : 
     353                 :          0 :         return 0;
     354                 :            : }
     355                 :            : 
     356                 :          0 : static int run(int argc, char *argv[]) {
     357                 :          0 :         _cleanup_(pstore_entries_reset) PStoreList list = {};
     358                 :            :         int r;
     359                 :            : 
     360                 :          0 :         log_setup_service();
     361                 :            : 
     362         [ #  # ]:          0 :         if (argc > 1)
     363         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     364                 :            :                                        "This program takes no arguments.");
     365                 :            : 
     366                 :            :         /* Ignore all parse errors */
     367                 :          0 :         (void) parse_config();
     368                 :            : 
     369         [ #  # ]:          0 :         log_debug("Selected storage '%s'.", pstore_storage_to_string(arg_storage));
     370         [ #  # ]:          0 :         log_debug("Selected Unlink '%d'.", arg_unlink);
     371                 :            : 
     372         [ #  # ]:          0 :         if (arg_storage == PSTORE_STORAGE_NONE)
     373                 :            :                 /* Do nothing, intentionally, leaving pstore untouched */
     374                 :          0 :                 return 0;
     375                 :            : 
     376                 :            :         /* Obtain list of files in pstore */
     377                 :          0 :         r = list_files(&list, arg_sourcedir);
     378         [ #  # ]:          0 :         if (r < 0)
     379                 :          0 :                 return r;
     380                 :            : 
     381                 :            :         /* Handle each pstore file */
     382                 :            :         /* Sort files lexigraphically ascending, generally needed by all */
     383                 :          0 :         qsort_safe(list.entries, list.n_entries, sizeof(PStoreEntry), compare_pstore_entries);
     384                 :            : 
     385                 :            :         /* Process known file types */
     386                 :          0 :         process_dmesg_files(&list);
     387                 :            : 
     388                 :            :         /* Move left over files out of pstore */
     389         [ #  # ]:          0 :         for (size_t n = 0; n < list.n_entries; n++)
     390                 :          0 :                 move_file(&list.entries[n], NULL);
     391                 :            : 
     392                 :          0 :         return 0;
     393                 :            : }
     394                 :            : 
     395                 :          0 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14