dump_systemstate reads extra tasks from config 96/203096/9
authorMichal Bloch <m.bloch@samsung.com>
Wed, 3 Apr 2019 09:46:47 +0000 (11:46 +0200)
committerMichal Bloch <m.bloch@samsung.com>
Mon, 29 Apr 2019 15:26:54 +0000 (17:26 +0200)
Change-Id: I0cf2f0411218e0ce6d0b7c0e940ea5e84f48fdf2
Signed-off-by: Michal Bloch <m.bloch@samsung.com>
include/defs.h.in
packaging/crash-worker.spec
src/crash-manager/crash-manager.c
src/dump_systemstate/CMakeLists.txt
src/dump_systemstate/dump_systemstate.c
src/dump_systemstate/dump_systemstate.h [new file with mode: 0644]
src/dump_systemstate/extras.c [new file with mode: 0644]
src/dump_systemstate/extras.h [new file with mode: 0644]
src/dump_systemstate/files.conf.example [new file with mode: 0644]
src/dump_systemstate/programs.conf.example [new file with mode: 0644]
src/log_dump/log_dump.c

index 25e465e..587172d 100644 (file)
@@ -7,6 +7,7 @@
 #define SYS_ASSERT                     "@SYS_ASSERT@"
 #define CRASH_STACK_PATH               "@CRASH_STACK_PATH@"
 #define CRASH_MANAGER_CONFIG_PATH      "@CRASH_MANAGER_CONFIG_PATH@"
+#define DUMP_SYSTEMSTATE_CONFIG_DIR_PATH               "@DUMP_SYSTEMSTATE_CONFIG_DIR_PATH@"
 #define MINICOREDUMPER_BIN_PATH                "@MINICOREDUMPER_BIN_PATH@"
 #define MINICOREDUMPER_CONFIG_PATH     "@MINICOREDUMPER_CONFIG_PATH@"
 #define DEBUGMODE_PATH                 "@DEBUGMODE_PATH@"
index efbb324..d6e7e7a 100644 (file)
@@ -104,6 +104,7 @@ export CFLAGS+=" -Werror"
           -DTMP_FILES_DIR=%{_sysconfdir}/tmpfiles.d \
           -DARCH=%{ARCH} \
           -DARCH_BIT=%{ARCH_BIT} \
+          -DDUMP_SYSTEMSTATE_CONFIG_DIR_PATH=%{_sysconfdir}/dump_systemstate.conf.d \
           -DCRASH_MANAGER_CONFIG_PATH=%{_sysconfdir}/crash-manager.conf \
           -DCRASH_ROOT_PATH=%{crash_root_path} \
           -DCRASH_PATH=%{crash_path} \
@@ -181,6 +182,8 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload
 %attr(-,root,root) %{_prefix}/lib/sysctl.d/99-crash-manager.conf
 %attr(0750,system_fw,system_fw) %{_bindir}/crash-manager
 %attr(0750,system_fw,system_fw) %{_bindir}/dump_systemstate
+%{_sysconfdir}/dump_systemstate.conf.d/files/files.conf.example
+%{_sysconfdir}/dump_systemstate.conf.d/programs/programs.conf.example
 %{_libexecdir}/crash-stack
 %{_libexecdir}/crash-popup-launch
 %{_libexecdir}/crash-notify-send
index 321dd4b..ea5b10a 100644 (file)
@@ -461,7 +461,7 @@ static void launch_crash_popup(struct crash_info *cinfo)
 
 static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid)
 {
-       char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-p", "-f",  cinfo->log_path, NULL};
+       char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-p", "-e", "-f",  cinfo->log_path, NULL};
        return spawn(av, NULL, NULL, NULL, pid, NULL);
 }
 
index 60737cb..7c3fa21 100755 (executable)
@@ -4,12 +4,17 @@ PROJECT(dump_systemstate C)
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src)
 SET(SRCS
        dump_systemstate.c
+       extras.c
        ${CMAKE_SOURCE_DIR}/src/shared/util.c
        ${CMAKE_SOURCE_DIR}/src/shared/spawn.c
    )
 
 INCLUDE(FindPkgConfig)
-pkg_check_modules(dump_systemstate_pkgs REQUIRED dlog libunwind)
+pkg_check_modules(dump_systemstate_pkgs REQUIRED
+       dlog
+       iniparser
+       libunwind
+)
 
 FOREACH(flag ${dump_systemstate_pkgs_CFLAGS})
        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
@@ -26,3 +31,9 @@ TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${dump_systemstate_pkgs_LDFLAGS} -pie)
 INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin
                PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
                GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/${PROJECT_NAME}/files.conf.example
+                          DESTINATION ${DUMP_SYSTEMSTATE_CONFIG_DIR_PATH}/files
+                          PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/${PROJECT_NAME}/programs.conf.example
+                          DESTINATION ${DUMP_SYSTEMSTATE_CONFIG_DIR_PATH}/programs
+                          PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
index 0d79cca..b853080 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * dump_systemstate
  *
- * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2012 - 2019 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the License);
  * you may not use this file except in compliance with the License.
 #include <sys/stat.h>
 #include <sys/vfs.h>
 
+#include "dump_systemstate.h"
+#include "extras.h"
 #include "shared/util.h"
 #include "shared/log.h"
 #include "shared/spawn.h"
 
 #define FILE_PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
 
-#define TIMEOUT_DEFAULT_MS (60*1000) /* 60sec */
-
-
-enum {
-       EXIT_OK      = 0,        // all ok
-       EXIT_ERR     = (1 << 0), // setup error
-       EXIT_FILEERR = (1 << 1), // at least one file failed to be copied
-       EXIT_CMDERR  = (1 << 2), // at least one command failed
-};
-
 static struct dump_item {
        const char *title;
        const char *path;
@@ -67,12 +59,15 @@ static struct dump_item {
 
 static void usage()
 {
-       fprintf(stderr, "usage: dump_systemstate [-k] [-d] [-j] [-p] [-f file]\n"
+       fprintf(stderr, "usage: dump_systemstate [-k] [-d] [-j] [-p] [-e] [-f file]\n"
                        "  -f: write to file (instead of stdout)\n"
                        "  -k: dump kernel messages (only root)\n"
                        "  -d: dump dlog messages\n"
                        "  -j: dump journal log messages\n"
                        "  -p: dump list of installed packages\n"
+                       "  -e: dump extras defined in the config\n"
+                       "      at  " DUMP_SYSTEMSTATE_CONFIG_DIR_PROGRAMS_PATH "\n"
+                       "      and " DUMP_SYSTEMSTATE_CONFIG_DIR_FILES_PATH    "\n"
           );
 }
 
@@ -99,6 +94,7 @@ int main(int argc, char *argv[])
        int out_fd = -1;
        bool arg_dlog = false;
        bool arg_dmesg = false;
+       bool arg_extras = false;
        bool arg_journal = false;
        bool arg_pkgs = false;
        char timestr[80];
@@ -106,7 +102,7 @@ int main(int argc, char *argv[])
        struct tm gm_tm;
        struct tm loc_tm;
 
-       while ((c = getopt(argc, argv, "hf:kdjp")) != -1) {
+       while ((c = getopt(argc, argv, "hf:kdjep")) != -1) {
                switch (c) {
                case 'd':
                        arg_dlog = true;
@@ -114,6 +110,9 @@ int main(int argc, char *argv[])
                case 'k':
                        arg_dmesg = true;
                        break;
+               case 'e':
+                       arg_extras = true;
+                       break;
                case 'j':
                        arg_journal = true;
                        break;
@@ -165,6 +164,9 @@ int main(int argc, char *argv[])
        }
        fprintf_fd(out_fd, "\n");
 
+       if (arg_extras)
+               exit_code |= handle_extra_dir(out_fd, DUMP_SYSTEMSTATE_CONFIG_DIR_FILES_PATH, handle_extra_file);
+
 #define spawn_wait_checked(av, env) \
        do { \
                int err; \
@@ -236,6 +238,9 @@ int main(int argc, char *argv[])
                spawn_wait_checked(journalctl_args, NULL);
        }
 
+       if (arg_extras)
+               exit_code |= handle_extra_dir(out_fd, DUMP_SYSTEMSTATE_CONFIG_DIR_PROGRAMS_PATH, handle_extra_program);
+
 #undef spawn_wait_checked
 
        if (arg_file)
diff --git a/src/dump_systemstate/dump_systemstate.h b/src/dump_systemstate/dump_systemstate.h
new file mode 100644 (file)
index 0000000..3ad833e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * This file is a part of dump_systemstate from the crash-worker project.
+ *
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+enum {
+       EXIT_OK      = 0,        // all ok
+       EXIT_ERR     = (1 << 0), // setup error
+       EXIT_FILEERR = (1 << 1), // at least one file failed to be copied
+       EXIT_CMDERR  = (1 << 2), // at least one command failed
+       EXIT_CONFERR = (1 << 3), // at least one config entry was invalid
+};
+
diff --git a/src/dump_systemstate/extras.c b/src/dump_systemstate/extras.c
new file mode 100644 (file)
index 0000000..6033485
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * This file is a part of dump_systemstate from the crash-worker project.
+ *
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// dump_systemstate
+#include "dump_systemstate.h"
+#include "extras.h"
+
+// crash-worker
+#include "shared/spawn.h"
+#include "shared/util.h"
+
+// external projects
+#include <iniparser.h>
+
+// POSIX
+#include <dirent.h>
+#include <fcntl.h>
+
+// C
+#include <assert.h>
+#include <stdbool.h>
+
+static inline void cleanup_dictionary(dictionary **ini)
+{
+       assert(ini);
+       if (!*ini)
+               return;
+
+       iniparser_freedict(*ini);
+}
+
+enum ini_fields {
+       INI_FIELD_TITLE = 0,
+       INI_FIELD_PATH,
+       INI_FIELD_ARGS,
+       INI_FIELD_ENV,
+       COUNT_INI_FIELDS,
+};
+
+static const char *const INI_KEYS[COUNT_INI_FIELDS] = {
+       [INI_FIELD_TITLE] = "title",
+       [INI_FIELD_PATH]  = "path",
+       [INI_FIELD_ARGS]  = "args",
+       [INI_FIELD_ENV]   = "env",
+};
+static const size_t MAX_INI_KEY_LEN = 5;
+
+struct extra_dump_item {
+       // not separate named fields, for convenient iteration
+       char *fields[COUNT_INI_FIELDS];
+};
+
+int handle_extra_program(int out_fd, struct extra_dump_item *item)
+{
+       assert(out_fd >= 0);
+       assert(item);
+
+       char *const title = item->fields[INI_FIELD_TITLE];
+       char *const path  = item->fields[INI_FIELD_PATH];
+       char *const args  = item->fields[INI_FIELD_ARGS] ?: "";
+       char *const env   = item->fields[INI_FIELD_ENV] ?: "";
+
+       if (!title || !path) {
+               fprintf_fd(out_fd, "\nNo title or path in extra program config");
+               return EXIT_CONFERR;
+       }
+
+       char *command_line;
+       int printed = asprintf(&command_line, "%s %s %s", env, path, args);
+       if (printed < 0) {
+               fprintf_fd(out_fd, "\nError, out of memory");
+               return EXIT_ERR;
+       }
+
+       fprintf_fd(out_fd, "\n==== %s (%s)\n", title, command_line);
+
+       /* Both `args` and `env` are single strings (because `iniparser` does not
+        * offer much when it comes to hierarchies and arrays) but `execve` expects
+        * an array of char pointers. Splitting isn't trivial (consider a brutal set
+        * of arguments using " or `) and I don't want to reinvent the wheel so I'm
+        * delegating the splitting to the shell. */
+       char *argv[] = {"/bin/sh", "-c", command_line, NULL};
+
+       int err;
+       spawn_param_u param = { .int_val = out_fd };
+       bool failed = !spawn_wait(argv, NULL, spawn_setstdout, &param, DEFAULT_COMMAND_TIMEOUT_MS, &err) || err != 0;
+
+       free(command_line);
+       return failed ? EXIT_CMDERR : 0;
+}
+
+int handle_extra_file(int out_fd, struct extra_dump_item *item)
+{
+       assert(out_fd >= 0);
+       assert(item);
+
+       char *const title = item->fields[INI_FIELD_TITLE];
+       char *const path  = item->fields[INI_FIELD_PATH];
+       if (!title || !path) {
+               fprintf_fd(out_fd, "\nNo title or path in extra file config");
+               return EXIT_CONFERR;
+       }
+
+       fprintf_fd(out_fd, "\n==== %s (%s)\n", title, path);
+       int ret = dump_file_write_fd(out_fd, (char *)path);
+       if (ret < 0) {
+               fprintf_fd(out_fd, "Unable to copy file.\n");
+               return EXIT_FILEERR;
+       }
+       return 0;
+}
+
+typedef int (*handle_ini_section_t)(int out_fd, struct extra_dump_item *);
+
+static int handle_ini_Nth_section(int out_fd, dictionary *ini, int n, handle_ini_section_t handle_ini_section)
+{
+       assert(out_fd >= 0);
+       assert(ini);
+       assert(n >= 0);
+       assert(n < iniparser_getnsec(ini));
+       assert(handle_ini_section);
+
+       char *const secname = iniparser_getsecname(ini, n);
+       assert(secname); // can only be NULL if `ini` is NULL or `n` is outta bounds
+
+       const size_t secname_len = strlen(secname);
+       char key_buf[secname_len + sizeof ':' + MAX_INI_KEY_LEN + sizeof '\0'];
+       memcpy(key_buf, secname, secname_len);
+       key_buf[secname_len] = ':';
+
+       char *const key_suffix_ptr = key_buf + secname_len + 1;
+       struct extra_dump_item item;
+       for (size_t i = 0; i < ARRAY_SIZE(item.fields); ++i) {
+               strcpy(key_suffix_ptr, INI_KEYS[i]);
+               item.fields[i] = iniparser_getstring(ini, key_buf, NULL);
+       }
+
+       return handle_ini_section(out_fd, &item);
+}
+
+static int handle_extra_ini(int out_fd, const char *ini_path, handle_ini_section_t handle_ini_section)
+{
+       assert(out_fd >= 0);
+       assert(ini_path);
+       assert(handle_ini_section);
+
+       __attribute__((cleanup(cleanup_dictionary))) dictionary *ini = iniparser_load(ini_path);
+       if (!ini) {
+               fprintf_fd(out_fd, "\nCouldn't parse ini file %s", ini_path);
+               return EXIT_CONFERR;
+       }
+
+       const int nsec = iniparser_getnsec(ini);
+       assert(nsec >= 0); // can only be -1 when ini is NULL
+
+       int ret = 0;
+       for (int i = 0; i < nsec; ++i)
+               ret |= handle_ini_Nth_section(out_fd, ini, i, handle_ini_section);
+       return ret;
+}
+
+static int config_entry_filter(const struct dirent *de)
+{
+       assert(de);
+       return de->d_type == DT_REG && string_ends_with(de->d_name, ".conf");
+}
+
+int handle_extra_dir(int out_fd, char *dir_path, handle_ini_section_t handle_ini_section)
+{
+       assert(out_fd >= 0);
+       assert(dir_path);
+       assert(handle_ini_section);
+
+       const int dir_fd = open(dir_path, O_DIRECTORY | O_RDONLY);
+       if (dir_fd < 0) {
+               fprintf_fd(out_fd, "\nCouldn't open extras dir: %s %m", dir_path);
+               return EXIT_ERR;
+       }
+
+       struct dirent **entries;
+       int entry_count = scandirat(dir_fd, ".", &entries, config_entry_filter, alphasort);
+       if (entry_count < 0) {
+               fprintf_fd(out_fd, "\nCouldn't process directory %s: %m", dir_path);
+               close(dir_fd);
+               return EXIT_ERR;
+       }
+
+       int ret = 0;
+       for (int i = 0; i < entry_count; ++i) {
+               struct dirent *const de = entries[i];
+               char ini_path[PATH_MAX];
+
+               /* In theory this introduces a race condition (somebody could replace
+                * folders between `scandirat` and the file being read) but `iniparser`
+                * does not offer any other way to open a file. Not letting the system
+                * get so brutally pwned is not our responsibility, at any rate. */
+               snprintf(ini_path, sizeof ini_path, "%s/%s", dir_path, de->d_name);
+               free(de);
+
+               ret |= handle_extra_ini(out_fd, ini_path, handle_ini_section);
+       }
+       free(entries);
+       close(dir_fd);
+       return ret;
+}
+
diff --git a/src/dump_systemstate/extras.h b/src/dump_systemstate/extras.h
new file mode 100644 (file)
index 0000000..c6d78de
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * This file is a part of dump_systemstate from the crash-worker project.
+ *
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "defs.h"
+
+#define DUMP_SYSTEMSTATE_CONFIG_DIR_PROGRAMS_PATH \
+       DUMP_SYSTEMSTATE_CONFIG_DIR_PATH "/programs"
+#define DUMP_SYSTEMSTATE_CONFIG_DIR_FILES_PATH \
+       DUMP_SYSTEMSTATE_CONFIG_DIR_PATH "/files"
+
+struct extra_dump_item;
+
+typedef int (*handle_ini_section_t)(int out_fd, struct extra_dump_item *);
+
+int handle_extra_dir(int out_fd, char *dir_path, handle_ini_section_t handle_ini_section);
+int handle_extra_file(int out_fd, struct extra_dump_item *item);
+int handle_extra_program(int out_fd, struct extra_dump_item *item);
+
diff --git a/src/dump_systemstate/files.conf.example b/src/dump_systemstate/files.conf.example
new file mode 100644 (file)
index 0000000..92d5f22
--- /dev/null
@@ -0,0 +1,8 @@
+[UNIQUE_ID_KEY]
+title=header line that gets printed (path gets appended too)
+path=/path/to/the/file
+
+[DLOG_CONF]
+title=dlog configuration file
+path=/opt/etc/dlog.conf
+
diff --git a/src/dump_systemstate/programs.conf.example b/src/dump_systemstate/programs.conf.example
new file mode 100644 (file)
index 0000000..8e26083
--- /dev/null
@@ -0,0 +1,10 @@
+[UNIQUE_ID_KEY]
+title=header line describing the program (will be printed alongside env, path and args)
+path=/path/to/the/program/executable
+args=-x foo --verbose
+env=POSIXLY_CORRECT=1
+
+[DLOG_DUMP]
+title=dump dlog contents
+path=/usr/bin/dlogutil
+args=-d
index 868e163..c20149f 100644 (file)
@@ -204,7 +204,7 @@ static bool dump_systemstate(const char *const destdir, const char *const timest
                return false;
        }
 
-       char *av[] = {"/usr/bin/dump_systemstate", "-k", "-d", "-j", "-f", dump_path, NULL};
+       char *av[] = {"/usr/bin/dump_systemstate", "-k", "-d", "-j", "-e", "-f", dump_path, NULL};
        bool is_ok = spawn_wait(av, NULL, NULL, NULL, 0, exit_code);
 
        free(dump_path);