/*
* log_dump: dump current system states
*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2016, 2018 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.
* limitations under the License.
*/
-#include <stdio.h>
-#include <stdlib.h>
+#include <assert.h>
+#include <dirent.h>
+#include <fcntl.h>
#include <getopt.h>
+#include <libgen.h>
#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
-#include <dirent.h>
-#include <libgen.h>
+
#include <system_info.h>
-#include "shared/util.h"
+#include <tzplatform_config.h>
+
#include "log_dump.h"
#include "dbus-handler.h"
+#include "shared/spawn.h"
+#include "shared/util.h"
-#undef LOG_TAG
-#define LOG_TAG "LOG_DUMP"
#define SYSTEM_INFO_KEY_BUILD_STRING "http://tizen.org/system/build.string"
+#define DIR_UMASK 0022
static const struct option opts[] = {
{ "normal", no_argument, 0, OPT_NORMAL },
{ 0, 0, 0, 0 }
};
-static inline void usage(void)
+/* tzplaform vars */
+char *dir_scripts;
+/* dynamic vars */
+char *dir_dump;
+char *dir_log;
+char *dir_debug;
+char *dir_temp; // temp rootdir
+char *dir_temp_logdump; // rootdir for this log_dump invocation
+char *version_string;
+/* timestamp */
+const char timestamp_format[] = "%Y%m%d%H%M%S";
+char timestamp_string[20]; /* as per format above */
+
+static bool init_temp_dir(char *const temp_root, char **temp_dir)
{
- printf("Usage: log_dump [OPTION]\n");
- printf("Dump options:\n");
- printf(" %-10s %s (%s)\n", "--normal",
- "dump all logs", DUMP_SCRIPTS_DIR);
- printf(" %-10s %s\n", "--short",
- "dump systemstate only");
- printf(" %-10s %s\n", "--dbus",
- "activate dbus interface");
+ assert(temp_root);
+ assert(temp_dir);
+
+ char *template = NULL, *path = NULL;
+ if (asprintf(&template, "%s/log.XXXXXX", temp_root) > 0)
+ path = mkdtemp(template);
+
+ if (!path) {
+ _E("Unable to create temporary directory at mkdtemp(%s): %m", template);
+ free(template);
+ return false;
+ }
+
+ *temp_dir = path;
+ return true;
+}
+
+static char *crash_root_get(void)
+{
+ return strdup(tzplatform_getenv(TZ_SYS_CRASH_ROOT));
}
-static int dump_scripts(void)
+static bool init_vars(const char *crash_root)
+{
+ if (!crash_root)
+ return false;
+
+ if (asprintf(&dir_log, "%s/log", crash_root) <= 0
+ || asprintf(&dir_debug, "%s/debug", crash_root) <= 0
+ || asprintf(&dir_dump, "%s/dump", crash_root) <= 0
+ || asprintf(&dir_temp, "%s/temp", crash_root) <= 0)
+ goto fail;
+
+ if (!init_temp_dir(dir_temp, &dir_temp_logdump))
+ goto fail;
+
+ make_dir(dir_temp_logdump, "log", DIR_UMASK);
+ make_dir(crash_root, "debug", DIR_UMASK);
+
+ _D("config: dir_log is %s", dir_log);
+ _D("config: dir_dump is %s", dir_dump);
+ _D("config: dir_debug is %s", dir_debug);
+ _D("config: dir_temp is %s", dir_temp);
+ _D("config: dir_temp_logdump is %s", dir_temp_logdump);
+
+ dir_scripts = strdup(tzplatform_getenv(TZ_SYS_DUMPGEN));
+ _D("config: dir_scripts is %s", dir_scripts);
+
+ if (system_info_get_platform_string(SYSTEM_INFO_KEY_BUILD_STRING, &version_string) != SYSTEM_INFO_ERROR_NONE) {
+ _W("Failed to system_info_get_platform_string for " SYSTEM_INFO_KEY_BUILD_STRING);
+ version_string = NULL;
+ }
+ _D("version_string is %s", version_string);
+
+ time_t cur_time;
+ struct tm loc_tm;
+ cur_time = time(NULL);
+ localtime_r(&cur_time, &loc_tm);
+ strftime(timestamp_string, sizeof(timestamp_string), timestamp_format, &loc_tm);
+ _D("timestamp_string is %s", timestamp_string);
+
+ assert(dir_log);
+ assert(dir_dump);
+ assert(dir_debug);
+ assert(dir_temp);
+ assert(dir_temp_logdump);
+ return true;
+
+fail:
+ free(dir_log);
+ free(dir_dump);
+ free(dir_debug);
+ free(dir_temp);
+ free(dir_temp_logdump);
+ return false;
+}
+
+static void usage(void)
+{
+ printf("Usage: log_dump {OPTION}\n"
+ "Options:\n"
+ " --normal dump all logs (uses scripts from %s)\n"
+ " --short dump_systemstate logs only\n"
+ " --dbus become dbus service\n",
+ dir_scripts);
+}
+
+static bool dump_scripts(char *const workdir, char *const scriptsdir)
{
struct dirent **dir_list = NULL;
char command[PATH_MAX];
int script_num, i;
- script_num = scandir(DUMP_SCRIPTS_DIR, &dir_list, NULL, NULL);
+ script_num = scandir(scriptsdir, &dir_list, NULL, NULL);
if (script_num < 0) {
- _E("Failed to scandir %s", DUMP_SCRIPTS_DIR);
- return -1;
+ _E("scandir %s: %m", scriptsdir);
+ return false;
}
for (i = 0; i < script_num; i++) {
- if (dir_list[i]->d_type != DT_REG)
+ const char *const name = dir_list[i]->d_name;
+ int type = dir_list[i]->d_type;
+
+ if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
continue;
- snprintf(command, sizeof(command), "%s/%s %s",
- DUMP_SCRIPTS_DIR, dir_list[i]->d_name,
- LOG_DUMP_DIR);
- _D("%s", command);
- system_command(command);
+ if (type != DT_REG) {
+ _D("Ignoring: not a regular file: %s", name);
+ continue;
+ }
+ snprintf(command, sizeof(command), "%s/%s", scriptsdir, name);
+ if (access(command, X_OK) != 0) {
+ _W("Ignoring: file not executable: %s", command);
+ continue;
+ }
+
+ _D("Calling scriptlet: %s", command);
+
+ char *const av[] = {command, workdir, NULL};
+ (void)spawn_wait(av, NULL, NULL, NULL, 0, NULL);
}
for (i = 0; i < script_num; i++)
free(dir_list[i]);
free(dir_list);
- return 0;
+ return true;
}
-int log_dump(int option)
+static bool dump_systemstate(const char *const destdir, const char *const timestr, int *exit_code)
{
- int ret;
- char *version_str = NULL;
- char *dump_dirname = NULL;
- char *crash_dirname = NULL;
- char timestr[80];
- char command[PATH_MAX];
- char dump_filename[NAME_MAX];
- time_t cur_time;
- struct tm loc_tm;
-
- broadcast_logdump_start();
+ char *dump_path = NULL;
- /* Make debug directory */
- if (access(LOG_DUMP_DIR, F_OK) != 0) {
- ret = snprintf(command, sizeof(command),
- "/usr/bin/mkdir -p %s", LOG_DUMP_DIR);
- if (ret < 0) {
- _E("Failed to mkdir");
- return -1;
- }
- system_command(command);
+ if (asprintf(&dump_path, "%s/log/dump_systemstate_%s.log", destdir, timestr) < 0) {
+ _E("asprintf: %m");
+ return false;
}
- /* Make result directory */
- if (access(LOG_DUMP_RESULT, F_OK) != 0) {
- ret = snprintf(command, sizeof(command),
- "/usr/bin/mkdir -p %s", LOG_DUMP_RESULT);
- if (ret < 0) {
- _E("Failed to mkdir");
- return -1;
- }
- system_command(command);
- }
+ char *av[] = {"/usr/bin/dump_systemstate", "-k", "-d", "-j", "-f", dump_path, NULL};
+ bool is_ok = spawn_wait(av, NULL, NULL, NULL, 0, exit_code);
- /* Get timestamp */
- cur_time = time(NULL);
- localtime_r(&cur_time, &loc_tm);
- strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", &loc_tm);
-
- /* Get version */
- ret = system_info_get_platform_string(SYSTEM_INFO_KEY_BUILD_STRING,
- &version_str);
- if (ret != SYSTEM_INFO_ERROR_NONE) {
- _E("Failed to system_info_get_platform_string");
- version_str = NULL;
- }
+ free(dump_path);
- /* Dump system states */
- ret = snprintf(command, sizeof(command),
- "/usr/bin/dump_systemstate -k -d -j -f "
- "%s/dump_systemstate_%s.log", LOG_DUMP_DIR, timestr);
- if (ret < 0) {
- _E("Failed to snprintf for command");
- goto exit;
- }
- system_command(command);
+ return is_ok;
+}
- /* Dump all logs */
- if (option == OPT_NORMAL)
- dump_scripts();
-
- if (version_str) {
- ret = snprintf(dump_filename, sizeof(dump_filename), "%s_%s",
- "log_dump", version_str);
- if (ret < 0) {
- _E("Failed to snprintf for dump path");
- goto exit;
- }
- } else {
- ret = snprintf(dump_filename, sizeof(dump_filename), "%s",
- "log_dump");
- if (ret < 0) {
- _E("Failed to snprintf for dump path");
- return -1;
- }
- }
+static bool compress(char *const destdir, char *const tempdir, char *const versionstr, char *const timestr, int *exit_code)
+{
+ char *archive_path = NULL;
- /* Compression */
- dump_dirname = strdup(LOG_DUMP_DIR);
- if (!dump_dirname) {
- _E("Failed to strdup for dump_dirname");
- goto exit;
+ if (asprintf(&archive_path, "%s/log_dump_%s%s.zip", destdir, versionstr ?: "", timestr) < 0) {
+ _E("asprintf: %m");
+ return false;
}
- if (option == OPT_NORMAL) {
- crash_dirname = strdup(CRASH_DUMP_DIR);
- if (!crash_dirname) {
- _E("Failed to strdup for dump_dirname");
- goto exit;
- }
+ _D("compress tempdir is %s", tempdir);
+ char *av[] = {"/bin/zip", "-yr", archive_path, ".", NULL};
+ bool is_ok = spawn_wait(av, NULL, spawn_chdir, (void*)tempdir, 0, exit_code);
- ret = snprintf(command, sizeof(command),
- "cd %s && /bin/zip -r %s/%s%s.zip %s %s > /dev/null 2>&1",
- LOG_DUMP_ROOT,
- LOG_DUMP_RESULT, dump_filename, timestr,
- basename(dump_dirname), basename(crash_dirname));
- if (ret < 0) {
- _E("Failed to snprintf for command");
- goto exit;
- }
- } else {
- ret = snprintf(command, sizeof(command),
- "cd %s && /bin/zip -r %s/%s%s.zip %s > /dev/null 2>&1",
- LOG_DUMP_ROOT,
- LOG_DUMP_RESULT, dump_filename, timestr,
- basename(dump_dirname));
- if (ret < 0) {
- _E("Failed to snprintf for command");
- goto exit;
- }
- }
- system_command(command);
+ _I("Storing report at %s", archive_path);
- sync();
+ fsync_path(archive_path);
+ free(archive_path);
- /* Remove gatherd dump */
- ret = remove_dir(LOG_DUMP_DIR, 0);
- if (ret < 0) {
- _E("Failed to delete dump directory");
- goto exit;
- }
- if (option == OPT_NORMAL) {
- ret = remove_dir(CRASH_DUMP_DIR, 0);
- if (ret < 0) {
- _E("Failed to delete crash dump directory");
- goto exit;
- }
- }
+ return is_ok;
+}
- broadcast_logdump_finish();
+int log_dump(int option)
+{
+ broadcast_logdump_start();
- /* Further operations for log_dump here */
+ int ret = -1;
-exit:
- if (version_str)
- free(version_str);
- if (dump_dirname)
- free(dump_dirname);
- if (crash_dirname)
- free(crash_dirname);
+ if (!dump_systemstate(dir_temp_logdump, timestamp_string, NULL))
+ goto out;
+
+ if (option == OPT_NORMAL)
+ (void)dump_scripts(dir_temp_logdump, dir_scripts);
+
+ compress(dir_debug, dir_temp_logdump, version_string, timestamp_string, NULL);
+
+ /* cleanup */
+ ret = remove_dir(dir_temp_logdump, 1);
+ if (ret < 0)
+ _W("Failed to delete dump directory at %s", dir_temp_logdump);
+
+ ret = 0;
+out:
+ broadcast_logdump_finish();
return ret;
}
int delete_dump(void)
{
- _I("delete_dump!");
+ _D("delete_dump called");
- remove_dir(LOG_DUMP_DIR, 0);
- remove_dir(LOG_DUMP_RESULT, 1);
- remove_dir(CRASH_DUMP_DIR, 0);
- remove_dir(CRASH_TEMP_DIR, 0);
+ remove_dir(dir_log, 0);
+ remove_dir(dir_debug, 1);
+ remove_dir(dir_dump, 0);
+ remove_dir(dir_temp, 0);
return 0;
}
+static void die(void)
+{
+ usage();
+ exit(EXIT_FAILURE);
+}
+
int main(int argc, char *argv[])
{
int c, ret;
int option;
- if (argc < 2) {
- usage();
- exit(EXIT_SUCCESS);
+ /* need to do this first, because even usage() uses the vars */
+ if (!init_vars(crash_root_get())) {
+ printf("Unable to initialize - please check program logs");
+ exit(EXIT_FAILURE);
}
+ if (argc < 2)
+ die();
+
option = -1;
while ((c = getopt_long_only(argc, argv, "", opts, NULL)) != -1) {
- switch (c) {
- case OPT_NORMAL:
- if (option >= 0) {
- usage();
- exit(EXIT_SUCCESS);
- }
- option = OPT_NORMAL;
- break;
- case OPT_SHORT:
- if (option >= 0) {
- usage();
- exit(EXIT_SUCCESS);
- }
- option = OPT_SHORT;
- break;
- case OPT_DBUS:
- if (option >= 0) {
- usage();
- exit(EXIT_SUCCESS);
- }
- option = OPT_DBUS;
- break;
- default:
- usage();
- exit(EXIT_SUCCESS);
- break;
- }
+ if (option >= 0 || c < _OPT_MIN || c > _OPT_MAX)
+ die();
+ option = c;
}
if (option == OPT_DBUS)
else
ret = log_dump(option);
- if (ret < 0)
- exit(EXIT_FAILURE);
-
- return 0;
+ return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}