[systemd-devel] [PATCH 3/6] pstore: The new systemd-pstore tool to archive pstore contents

Eric DeVolder eric.devolder at oracle.com
Thu May 16 14:28:32 UTC 2019


The systemd-pstore which is a tool that performs the following:
 - reads the pstore.conf configuration file
 - lists the files in the pstore (eg. /sys/fs/pstore)
 - for each file, locates a handler for the type of file (eg. dmesg, MCE, etc)
 - invokes the handler for the file (eg. the handler appends file to a list)
 - when the list of files is exhausted, all handlers are notified; in the case
   of the dmesg handler, final processing of the files occurs:
   - files sorted in reverse lexigraphical order to faciliate reconstruction
     of original dmesg
   - the filename is examined to determine which dmesg it is a part
   - the file is either moved to archive storage or recorded in the journal
   - the file is appended to the reconstructed dmesg

Signed-off-by: Eric DeVolder <eric.devolder at oracle.com>
---
 src/pstore/pstore.c | 736 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 736 insertions(+)
 create mode 100644 src/pstore/pstore.c

diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c
new file mode 100644
index 0000000..f2e9845
--- /dev/null
+++ b/src/pstore/pstore.c
@@ -0,0 +1,736 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+/*
+ * Generally speaking, the pstore contains a small number of files
+ * that in turn contain a small amount of data.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <sys/prctl.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include "sd-daemon.h"
+#include "sd-journal.h"
+#include "sd-login.h"
+#include "sd-messages.h"
+
+#include "acl-util.h"
+#include "alloc-util.h"
+#include "capability-util.h"
+#include "cgroup-util.h"
+#include "compress.h"
+#include "conf-parser.h"
+#include "copy.h"
+#include "dirent-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "io-util.h"
+#include "journal-importer.h"
+#include "log.h"
+#include "macro.h"
+#include "main-func.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "signal-util.h"
+#include "socket-util.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+#include "user-util.h"
+#include "util.h"
+
+#define ARRAY_SIZE(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0]))
+
+#define PATHSZ 1024
+#define ARG_SOURCEDIR_DEFAULT "/sys/fs/pstore"
+#define ARG_ARCHIVEDIR_DEFAULT "/var/lib/systemd/pstore"
+
+/*
+ * Command line argument handling
+ */
+typedef enum PstoreStorage {
+        PSTORE_STORAGE_NONE,
+        PSTORE_STORAGE_ARCHIVE,
+        PSTORE_STORAGE_JOURNAL,
+        _PSTORE_STORAGE_MAX,
+        _PSTORE_STORAGE_INVALID = -1
+} PstoreStorage;
+
+static const char* const pstore_storage_table[_PSTORE_STORAGE_MAX] = {
+        [PSTORE_STORAGE_NONE] = "none",
+        [PSTORE_STORAGE_ARCHIVE] = "archive",
+        [PSTORE_STORAGE_JOURNAL] = "journal",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(pstore_storage, PstoreStorage);
+static DEFINE_CONFIG_PARSE_ENUM(config_parse_pstore_storage, pstore_storage, PstoreStorage, "Failed to parse storage setting");
+
+static PstoreStorage arg_storage = PSTORE_STORAGE_ARCHIVE;
+
+static bool arg_allowunlink = true;
+static char *arg_sourcedir = NULL;
+static char *arg_archivedir = NULL;
+STATIC_DESTRUCTOR_REGISTER(arg_sourcedir, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_archivedir, freep);
+
+static int parse_config(void) {
+    int rc;
+    static const ConfigTableItem items[] = {
+            { "Pstore", "AllowUnlink",      config_parse_bool,              0, &arg_allowunlink },
+            { "Pstore", "Storage",          config_parse_pstore_storage,    0, &arg_storage     },
+            { "Pstore", "SourceDir",        config_parse_path,              0, &arg_sourcedir   },
+            { "Pstore", "ArchiveDir",       config_parse_path,              0, &arg_archivedir  },
+            {}
+    };
+
+    rc = config_parse_many_nulstr(PKGSYSCONFDIR "/pstore.conf",
+                                    CONF_PATHS_NULSTR("systemd/pstore.conf.d"),
+                                    "Pstore\0",
+                                    config_item_table_lookup, items,
+                                    CONFIG_PARSE_WARN, NULL);
+    if (NULL == arg_sourcedir)
+    {
+        arg_sourcedir = (char *)malloc(sizeof(ARG_SOURCEDIR_DEFAULT)+1);
+        if (NULL != arg_sourcedir)
+            strcpy(arg_sourcedir, ARG_SOURCEDIR_DEFAULT);
+    }
+    if (NULL == arg_archivedir)
+    {
+        arg_archivedir = (char *)malloc(sizeof(ARG_ARCHIVEDIR_DEFAULT)+1);
+        if (NULL != arg_archivedir)
+            strcpy(arg_archivedir, ARG_ARCHIVEDIR_DEFAULT);
+    }
+    return rc;
+}
+
+/*
+ * File list handling
+ */
+typedef struct dirent_cs
+{
+    struct dirent de;
+    struct stat statbuf;
+    int is_binary;
+    char *contents;
+    struct dirent_cs *next;
+    struct dirent_cs *prev;
+} dirent_ct;
+
+#define END_OF_DIRENT_LIST(LIST, DC) ((DC)->next == (LIST))
+#define START_OF_DIRENT_LIST(LIST, DC) ((DC) == (LIST))
+
+#define FOR_EACH_DIRENT_IN_LIST(LIST, VAR) \
+    for (VAR = LIST; VAR; VAR = (VAR->next == LIST) ? NULL : VAR->next)
+
+void dump_dirent_in_list (dirent_ct *list);
+void prepend_dirent_to_list (dirent_ct **list, dirent_ct *dc);
+void append_dirent_to_list (dirent_ct **list, dirent_ct *dc);
+void remove_dirent_from_list (dirent_ct **list, dirent_ct *dc);
+void sort_files (dirent_ct **list, int ascending);
+void free_files (dirent_ct **list);
+int move_file (dirent_ct *dc, const char *subdir);
+
+
+void
+dump_dirent_in_list (dirent_ct *list)
+{
+    dirent_ct *dc;
+    FOR_EACH_DIRENT_IN_LIST(list, dc)
+    {
+        printf(" %08x %8lu %s\n", dc->statbuf.st_mode, dc->statbuf.st_size, dc->de.d_name);
+    }
+}
+
+void
+prepend_dirent_to_list (dirent_ct **list, dirent_ct *dc)
+{
+    if (NULL == *list)
+    {
+        *list = dc;
+        dc->next = dc->prev = dc;
+    }
+    else
+    {
+        dirent_ct *first = *list;
+        dirent_ct *last = (*list)->prev;
+        dc->next = first;
+        dc->prev = last;
+        first->prev = dc;
+        last->next = dc;
+        *list = dc;
+    }
+}
+
+void
+append_dirent_to_list (dirent_ct **list, dirent_ct *dc)
+{
+    if (NULL == *list)
+    {
+        *list = dc;
+        dc->next = dc->prev = dc;
+    }
+    else
+    {
+        dirent_ct *first = *list;
+        dirent_ct *last = (*list)->prev;
+        dc->next = first;
+        dc->prev = last;
+        first->prev = dc;
+        last->next = dc;
+    }
+}
+
+void
+remove_dirent_from_list (dirent_ct **list, dirent_ct *dc)
+{
+    dirent_ct *prev = dc->prev;
+    dirent_ct *next = dc->next;
+    prev->next = next;
+    next->prev = prev;
+    *list = (*list == dc) ? ((dc->next == dc) ? NULL : next) : *list;
+}
+
+void
+sort_files (dirent_ct **list, int ascending)
+{
+    // Simple brute force linear sort
+    int sorted = 0;
+    dirent_ct *dc;
+    do
+    {
+        sorted = 1;
+        FOR_EACH_DIRENT_IN_LIST(*list, dc)
+        {
+            if (ascending)
+            {
+                if (!END_OF_DIRENT_LIST(*list, dc) &&
+                    /* lexigraphical sort, ascending */
+                    (strcmp(dc->de.d_name, dc->next->de.d_name) > 0))
+                {
+                    remove_dirent_from_list(list, dc);
+                    append_dirent_to_list(list, dc);
+                    sorted = 0;
+                    break; // re-start sort
+                }
+            }
+            else
+            {
+                if (!START_OF_DIRENT_LIST(*list, dc) &&
+                    /* lexigraphical sort, descending */
+                    (strcmp(dc->de.d_name, dc->prev->de.d_name) > 0))
+                {
+                    remove_dirent_from_list(list, dc);
+                    prepend_dirent_to_list(list, dc);
+                    sorted = 0;
+                    break; // re-start sort
+                }
+            }
+        }
+    } while (!sorted);
+}
+
+void
+free_files (dirent_ct **list)
+{
+    dirent_ct *dc;
+    FOR_EACH_DIRENT_IN_LIST(*list, dc)
+    {
+        if (NULL != dc->contents)
+        {
+            free(dc->contents);
+        }
+        remove_dirent_from_list(list, dc);
+        free(dc);
+        break; // re-start loop
+    }
+}
+
+int
+move_file (dirent_ct *dc, const char *subdir)
+{
+    char ifdpath[PATHSZ];
+    int remove_file = 0;
+    int rc = 0;
+
+    snprintf(ifdpath, sizeof(ifdpath), "%s/%s", arg_sourcedir, dc->de.d_name);
+
+    if (PSTORE_STORAGE_ARCHIVE == arg_storage)
+    {
+        /* This code copies the file from the pstore to other storage.
+       The rename() syscall is not utilized as it results in the
+       Invalid cross-device link error.
+       In addition, an optional subdirectory can be specified in
+       forming the final destination path.
+        */
+        char ofdpath[PATHSZ];
+
+        if (NULL != subdir)
+        {
+            snprintf(ofdpath, sizeof(ofdpath), "%s/%s/%s", arg_archivedir, subdir, dc->de.d_name);
+        }
+        else
+        {
+            snprintf(ofdpath, sizeof(ofdpath), "%s/%s", arg_archivedir, dc->de.d_name);
+        }
+
+        /* Make sure destination exists */
+        rc = mkdir_parents(ofdpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH /*0755*/);
+        if (0 == rc)
+        {
+            int ofd;
+
+            ofd = open(ofdpath, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH/*0644*/);
+            if (ofd < 0)
+            {
+                log_error("open(%s): %s\n", ofdpath, strerror(errno));
+                rc = errno;
+            }
+            else
+            {
+                if (NULL != dc->contents)
+                {
+                    ssize_t wrc;
+                    wrc = write(ofd, dc->contents, dc->statbuf.st_size);
+                    // Detect write problem
+                    if (wrc != dc->statbuf.st_size)
+                    {
+                        log_error("write(%s): %s\n", ofdpath, strerror(errno));
+                        rc = errno;
+                    }
+                    else
+                        remove_file = 1;
+                }
+            }
+            close(ofd);
+// FIX??? update ofd stat.time to same info as dc.stat.time?
+        }
+        else
+        {
+            log_error("mkdir_parents(): %s\n", strerror(errno));
+            rc = errno;
+        }
+    }
+    else
+    if (PSTORE_STORAGE_JOURNAL == arg_storage)
+    {
+        //sd_journal_send("MESSAGE=%s", dc->de.d_name, NULL);
+        if (dc->is_binary)
+        {
+            //WITH_BINARY= but how to know end of binary blob if contained in 0 terminated string?
+        }
+        else
+        {
+            rc = sd_journal_send("MESSAGE=File %s:\n%s", dc->de.d_name, dc->contents, NULL);
+        }
+        // NOTE: If journald not running, rc always 0
+        remove_file = (0 == rc);
+    }
+
+    /* If file copied properly, remove it from pstore */
+    if ((0 == rc) && remove_file && arg_allowunlink)
+    {
+        unlink(ifdpath);
+    }
+
+    return rc;
+}
+
+static void
+write_dmesg (const char *dmesg, ssize_t size, const char *id)
+{
+    if ((NULL != dmesg) && (size > 0))
+    {
+        char ofdpath[PATHSZ];
+        ssize_t wrc;
+        int ofd;
+
+        log_info("Record ID %s\n", id);
+
+        if (NULL != id)
+        {
+            snprintf(ofdpath, sizeof(ofdpath), "%s/%s/dmesg.txt", arg_archivedir, id);
+        }
+        else
+        {
+            snprintf(ofdpath, sizeof(ofdpath), "%s/dmesg.txt", arg_archivedir);
+        }
+
+        ofd = open(ofdpath, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH/*0644*/);
+        if (ofd < 0)
+        {
+            log_error("open(%s): %s\n", ofdpath, strerror(errno));
+        }
+        else
+        {
+            wrc = write(ofd, dmesg, size);
+            if (wrc != size)
+            {
+                log_error("write(%s): %s\n", ofdpath, strerror(errno));
+            }
+        }
+        close(ofd);
+    }
+}
+
+/*
+ * Pstore content rule handling
+ */
+typedef struct rules_s
+{
+    void (*handler)(struct rules_s *rule, dirent_ct *dc);
+    const char *handlerName;
+    const char *pattern;
+} rules_t;
+void dmesg_handler (struct rules_s *rule, dirent_ct *dc);
+void move_handler (struct rules_s *rule, dirent_ct *dc);
+rules_t rules[] =
+{
+    // from Linux/fs/pstore/inode.c
+    // the %s is record->psi->name (eg efi, erst)
+    // the %llu is record->id
+    { dmesg_handler, "dmesg", "dmesg-*", },     // PSTORE_TYPE_DMESG: "dmesg-%s-%llu%s",
+    { move_handler, "console", "console-*", },  // PSTORE_TYPE_CONSOLE: "console-%s-%llu",
+    { move_handler, "pmsg", "pmsg-*", },        // PSTORE_TYPE_PMSG: "pmsg-%s-%llu",
+    { move_handler, "ftrace", "ftrace-*", },    // PSTORE_TYPE_FTRACE: "ftrace-%s-%llu",
+    { move_handler, "mce", "mce-*", },          // PSTORE_TYPE_MCE: "mce-%s-%llu",
+    { move_handler, "ppcrtas", "rtas-*", },     // PSTORE_TYPE_PPC_RTAS: "rtas-%s-%llu",
+    { move_handler, "ppcof", "powerpc-ofw-*", },// PSTORE_TYPE_PPC_OF: "powerpc-ofw-%s-%llu",
+    { move_handler, "ppccommon", "powerpc-common-*", }, // PSTORE_TYPE_PPC_COMMON: "powerpc-common-%s-%llu",
+    { move_handler, "ppcopal", "powerpc-opal-*", },     // PSTORE_TYPE_PPC_OPAL: "powerpc-opal-%s-%llu",
+    { move_handler, "unknown", "unknown-*", },      // PSTORE_TYPE_UNKNOWN: "unknown-%s-%llu",
+};
+
+void
+dmesg_handler (struct rules_s *rule, dirent_ct *dc)
+{
+    /* dmesg-* files recorded on this list */
+    static dirent_ct *dmesgfiles = NULL;
+
+    if (NULL != dc)
+    {
+        /* Collect the file for later processing */
+        dc->is_binary = 0;
+        append_dirent_to_list(&dmesgfiles, dc);
+    }
+    else
+    {
+        /* End of files, move files, reconstruct dmesg.txt */
+        char *dmesg = NULL, *dmesg2;
+        ssize_t dmesgsize = 0;
+        char id1[PATHSZ], id2[PATHSZ], *id;
+        char *currentid;
+        char *p;
+
+        if (NULL == dmesgfiles) return;
+
+        id1[0] = id2[0] = '\0';
+        id = id1;
+
+        /* Sort in reverse order so as to be able to reconstruct dmesg */
+        sort_files(&dmesgfiles, 0);
+
+        /* Handle each file */
+        FOR_EACH_DIRENT_IN_LIST(dmesgfiles, dc)
+        {
+            int move_file_and_continue = 0;
+
+            if (endswith(dc->de.d_name, ".enc.z"))
+                move_file_and_continue = 1;
+            p = strrchr(dc->de.d_name, '-');
+            if (NULL == p)
+                move_file_and_continue = 1;
+
+            if (move_file_and_continue)
+            {
+                // A dmesg file on which we do not do additional processing
+                move_file(dc, NULL);
+                continue;
+            }
+
+            /* See if this file is one of a related group of files
+               in order to reconstruct dmesg */
+
+            /* When dmesg is written into pstore, it is done so
+            in small chunks, whatever the exchange buffer size is
+            with the underlying pstore backend (ie. EFI may be ~2KiB),
+            which means an example pstore with approximately 64KB of
+            storage may have up to roughly 32 dmesg files that could be
+            related, depending upon the size of the original dmesg.
+            Here we look at the dmesg filename and try to discern if
+            files are part of a related group, meaning the same original
+            dmesg.
+            The two known pstore backends are EFI and ERST. These backends
+            store data in the Common Platform Error Record, CPER, format.
+            The dmesg- filename contains the CPER record id, a 64bit number
+            (in decimal notation). In Linux, the record id is encoded with
+            two digits for the dmesg part (chunk) number and 3 digits for
+            the count number. So allowing an additional digit to compensate
+            for advancing time, this code ignores the last six digits of the
+            filename in determining the record id.
+            For the EFI backend, the record id encodes an id
+            in the upper 32 bits, and a timestamp in the lower 32-bits.
+            So ignoring the least significant 6 digits has proven to
+            generally identify related dmesg entries.
+            */
+#define PSTORE_FILENAME_IGNORE 6
+
+            /* extract common portion of record id */
+            currentid = NULL;
+            if (strlen(p) > PSTORE_FILENAME_IGNORE)
+            {
+                currentid = (id == id1) ? id2 : id1;
+                strcpy(currentid, p+1);
+                currentid[strlen(currentid) - PSTORE_FILENAME_IGNORE] = '\0';
+            }
+
+            /* Now move file from pstore to archive storage */
+            move_file(dc, currentid);
+
+            /* If the current record id is NOT the same as the
+                previous record id, then start a new dmesg.txt file
+            */
+            if (0 != strcmp(currentid, id))
+            {
+                /* Encountered a new dmesg group, close out old one, open new one */
+                if (NULL != dmesg)
+                {
+                    write_dmesg(dmesg, dmesgsize, id);
+                    free(dmesg);
+                    dmesg = NULL;
+                    dmesgsize = 0;
+                }
+
+                /* Swap record id pointers */
+                id[0] = '\0'; // invalidate previous record id
+                id = (id == id1) ? id2 : id1; // make current id
+            }
+
+            /* Reconstruction of dmesg is done as a useful courtesy, do not log errors */
+            dmesg2 = (char *)realloc(dmesg, dmesgsize + strlen(dc->de.d_name) + sizeof(":\n"));
+            if (NULL != dmesg2)
+            {
+                dmesg = dmesg2;
+                dmesgsize += sprintf(&dmesg[dmesgsize], "%s:\n", dc->de.d_name);
+                dmesg2 = realloc(dmesg, dmesgsize + dc->statbuf.st_size);
+                if (NULL != dmesg2)
+                {
+                    dmesg = dmesg2;
+                    memcpy(&dmesg[dmesgsize], dc->contents, dc->statbuf.st_size);
+                    dmesgsize += dc->statbuf.st_size;
+                }
+            }
+        }
+        if (NULL != dmesg)
+        {
+            write_dmesg(dmesg, dmesgsize, id);
+            free(dmesg);
+        }
+
+        free_files(&dmesgfiles);
+    }
+}
+
+void
+move_handler (struct rules_s *rule, dirent_ct *dc)
+{
+    /* Simply move file out of pstore into archive storage */
+    static dirent_ct *movefiles = NULL;
+
+    if (NULL != dc)
+    {
+        append_dirent_to_list(&movefiles, dc);
+    }
+    else
+    {
+        // end of files
+        FOR_EACH_DIRENT_IN_LIST(movefiles, dc)
+        {
+            move_file(dc, NULL);
+        }
+        free_files(&movefiles);
+    }
+}
+
+static int
+list_files (dirent_ct **list, const char *sourcepath)
+{
+    DIR *dirp;
+    int rc = 0;
+
+    errno = 0;
+    dirp = opendir(sourcepath);
+    if (NULL == dirp)
+    {
+        log_error("opendir(%s): %s\n", sourcepath, strerror(errno));
+        rc = errno;
+    }
+    else
+    {
+        struct dirent *de;
+        dirent_ct *dc;
+        do
+        {
+            errno = 0;
+            de = readdir(dirp);
+            if ((NULL != de) && (NULL == startswith(de->d_name, ".")))
+            {
+                char pathname[PATHSZ];
+                struct stat statbuf;
+
+                snprintf(pathname, sizeof(pathname), "%s/%s", sourcepath, de->d_name);
+                if (0 == lstat(pathname, &statbuf))
+                {
+                    if (NULL != (dc = (dirent_ct *)malloc(sizeof(dirent_ct))))
+                    {
+                        dc->de = *de;
+                        dc->statbuf = statbuf;
+                        dc->is_binary = 1;
+                        append_dirent_to_list(list, dc);
+
+                        /* Now read contents of pstore file */
+                        dc->contents = (char *)malloc(dc->statbuf.st_size);
+                        if (NULL != dc->contents)
+                        {
+                            int ifd;
+                            ssize_t rrc;
+
+                            ifd = open(pathname, O_RDONLY);
+                            if (ifd < 0)
+                            {
+                                log_error("open(%s): %s\n", pathname, strerror(errno));
+                            }
+                            else
+                            {
+                                rrc = read(ifd, dc->contents, dc->statbuf.st_size);
+                                if (rrc != dc->statbuf.st_size)
+                                {
+                                    log_error("read(%s, %ld) only returned %ld bytes\n", pathname, dc->statbuf.st_size, rrc);
+// FIX??? do we keep partial bytes or throw all away?
+                                }
+                            }
+                        }
+                        else
+                        {
+                            log_error("malloc(%s, %ld): failed\n", pathname, dc->statbuf.st_size);
+                        }
+                    }
+                    else
+                    {
+                        log_error("malloc() failed: out of memory!\n");
+                    }
+                }
+                else
+                {
+                    log_error("lstat(%s): %s\n", pathname, strerror(errno));
+                    rc = errno;
+                    break;
+                }
+            }
+            else
+            {
+                if (errno)
+                {
+                    log_error("readdir(): %s\n", strerror(errno));
+                    rc = errno;
+                    break;
+                }
+                /* else end of dir */
+            }
+        } while (NULL != de);
+    }
+    return rc;
+}
+
+static int
+run (int argc, char *argv[])
+{
+    unsigned i;
+    int process_pstore = 0;
+    int rc = 0;
+    dirent_ct *fileList = NULL;
+
+    log_open();
+
+    /* Ignore all parse errors */
+    (void) parse_config();
+
+    log_debug("Selected storage '%s'.", pstore_storage_to_string(arg_storage));
+    log_debug("Selected SourceDir '%s'.", arg_sourcedir);
+    log_debug("Selected ArchiveDir '%s'.", arg_archivedir);
+    log_debug("Selected AllowUnlink '%d'.", arg_allowunlink);
+
+    if (PSTORE_STORAGE_NONE == arg_storage)
+    {
+        // Do nothing, intentionally, leaving pstore untouched
+        process_pstore = 0;
+    }
+    else
+    {
+        process_pstore = 1;
+        /* Obtain list of files in pstore */
+        rc = list_files(&fileList, arg_sourcedir);
+    }
+
+    /* Process pstore contents, if no errors in reading pstore */
+    if ((0 == rc) && process_pstore)
+    {
+        for (i = 0; i < ARRAY_SIZE(rules); ++i)
+        {
+            struct rules_s *rule = &rules[i];
+            int last_file = 0;
+            while (!last_file)
+            {
+                dirent_ct *dc;
+                int handle = 0;
+                FOR_EACH_DIRENT_IN_LIST(fileList, dc)
+                {
+                    char pattern[PATHSZ];
+                    int plen = strlen(rule->pattern);
+                    int starts = (rule->pattern[plen-1] == '*');
+                    int ends = (rule->pattern[0] == '*');
+
+                    handle = 0;
+                    last_file = END_OF_DIRENT_LIST(fileList, dc);
+
+                    if (starts)
+                    {
+                        strncpy(pattern, &rule->pattern[0], plen-1);
+                        pattern[plen-1] = '\0';
+                        handle |= (NULL != startswith(dc->de.d_name, pattern));
+                    }
+                    if (ends)
+                    {
+                        strncpy(pattern, &rule->pattern[1], plen-1);
+                        pattern[plen-1] = '\0';
+                        handle |= (NULL != endswith(dc->de.d_name, pattern));
+                    }
+                    if (handle)
+                    {
+                        /* handler will now own dc */
+                        remove_dirent_from_list(&fileList, dc);
+                        rule->handler(rule, dc);
+                        break; // re-start loop, since just modified the list
+                    }
+                    // else we leave unknown file alone...
+                }
+                if (!handle) last_file = 1;
+            }
+            /* Signal end of files for rule */
+            rule->handler(rule, NULL);
+        }
+        free_files(&fileList);
+    }
+
+    return rc;
+}
+
+DEFINE_MAIN_FUNCTION(run);
+
-- 
2.7.4



More information about the systemd-devel mailing list