[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