mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-11 13:03:15 +00:00
tools:acrn-crashlog: Improve the process of crash reclassify
1. Reload the trigger file(s) of subcrash while doing crash reclassify. 2. Support simple format for trigger file. Signed-off-by: Liu, Xinwu <xinwu.liu@intel.com> Acked-by: Chen Gang <gang.c.chen@intel.com>
This commit is contained in:
parent
0683b16573
commit
a5853d6d8a
@ -400,6 +400,7 @@ static void get_last_line_synced(const struct sender_t *sender)
|
|||||||
int sid;
|
int sid;
|
||||||
int ret;
|
int ret;
|
||||||
struct vm_t *vm;
|
struct vm_t *vm;
|
||||||
|
char *p;
|
||||||
char vmkey[ANDROID_WORD_LEN];
|
char vmkey[ANDROID_WORD_LEN];
|
||||||
char vm_name[32];
|
char vm_name[32];
|
||||||
|
|
||||||
@ -438,6 +439,9 @@ static void get_last_line_synced(const struct sender_t *sender)
|
|||||||
strerror(errno));
|
strerror(errno));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
p = strchr(vmkey, ' ');
|
||||||
|
if (p)
|
||||||
|
*p = 0;
|
||||||
|
|
||||||
ret = refresh_key_synced_stage1(sender, vm, vmkey, MM_ONLY);
|
ret = refresh_key_synced_stage1(sender, vm, vmkey, MM_ONLY);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
*
|
*
|
||||||
* @return 1 if find the same string, or 0 if not.
|
* @return 1 if find the same string, or 0 if not.
|
||||||
*/
|
*/
|
||||||
static int has_content(char *file, char *content)
|
static int has_content(const char *file, const char *content)
|
||||||
{
|
{
|
||||||
if (content && strstr(file, content))
|
if (content && strstr(file, content))
|
||||||
return 1;
|
return 1;
|
||||||
@ -44,12 +44,12 @@ static int has_content(char *file, char *content)
|
|||||||
*
|
*
|
||||||
* @return 1 if all configured strings were found, or 0 if not.
|
* @return 1 if all configured strings were found, or 0 if not.
|
||||||
*/
|
*/
|
||||||
static int crash_has_all_contents(struct crash_t *crash,
|
static int crash_has_all_contents(const struct crash_t *crash,
|
||||||
char *file)
|
const char *file)
|
||||||
{
|
{
|
||||||
int id;
|
int id;
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
char *content;
|
const char *content;
|
||||||
|
|
||||||
for_each_content_crash(id, content, crash) {
|
for_each_content_crash(id, content, crash) {
|
||||||
if (!content)
|
if (!content)
|
||||||
@ -78,14 +78,14 @@ static int crash_has_all_contents(struct crash_t *crash,
|
|||||||
*
|
*
|
||||||
* @return 1 if result is true, or 0 if false.
|
* @return 1 if result is true, or 0 if false.
|
||||||
*/
|
*/
|
||||||
static int crash_has_mightcontents(struct crash_t *crash,
|
static int crash_has_mightcontents(const struct crash_t *crash,
|
||||||
char *file)
|
const char *file)
|
||||||
{
|
{
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
int ret_exp;
|
int ret_exp;
|
||||||
int expid, cntid;
|
int expid, cntid;
|
||||||
char **exp;
|
char * const *exp;
|
||||||
char *content;
|
const char *content;
|
||||||
|
|
||||||
for_each_expression_crash(expid, exp, crash) {
|
for_each_expression_crash(expid, exp, crash) {
|
||||||
if (!exp || !exp_valid(exp))
|
if (!exp || !exp_valid(exp))
|
||||||
@ -119,13 +119,14 @@ static int crash_has_mightcontents(struct crash_t *crash,
|
|||||||
*
|
*
|
||||||
* @return 1 if file matches these strings configured in crash, or 0 if not.
|
* @return 1 if file matches these strings configured in crash, or 0 if not.
|
||||||
*/
|
*/
|
||||||
static int crash_reclassify(struct crash_t *crash, char *file)
|
static int crash_match_content(const struct crash_t *crash, const char *file)
|
||||||
{
|
{
|
||||||
return crash_has_all_contents(crash, file) &&
|
return crash_has_all_contents(crash, file) &&
|
||||||
crash_has_mightcontents(crash, file);
|
crash_has_mightcontents(crash, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _get_data(char *file, struct crash_t *crash, char **data, int index)
|
static int _get_data(const char *file, const struct crash_t *crash,
|
||||||
|
char **data, const int index)
|
||||||
{
|
{
|
||||||
char *search_key;
|
char *search_key;
|
||||||
char *value;
|
char *value;
|
||||||
@ -172,7 +173,7 @@ static int _get_data(char *file, struct crash_t *crash, char **data, int index)
|
|||||||
*
|
*
|
||||||
* @return 0 if successful, or errno if not.
|
* @return 0 if successful, or errno if not.
|
||||||
*/
|
*/
|
||||||
static int get_data(char *file, struct crash_t *crash,
|
static int get_data(const char *file, const struct crash_t *crash,
|
||||||
char **data0, char **data1, char **data2)
|
char **data0, char **data1, char **data2)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
@ -201,6 +202,62 @@ fail:
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct crash_t *crash_find_matched_child(const struct crash_t *crash,
|
||||||
|
const char *rtrfmt)
|
||||||
|
{
|
||||||
|
struct crash_t *child;
|
||||||
|
struct crash_t *matched_child = NULL;
|
||||||
|
int i;
|
||||||
|
int count;
|
||||||
|
int res;
|
||||||
|
const char *trfile_fmt;
|
||||||
|
char **trfiles;
|
||||||
|
void *content;
|
||||||
|
unsigned long size;
|
||||||
|
|
||||||
|
if (!crash)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for_crash_children(child, crash) {
|
||||||
|
if (!child->trigger)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!strcmp(child->trigger->type, "dir"))
|
||||||
|
trfile_fmt = rtrfmt;
|
||||||
|
else
|
||||||
|
trfile_fmt = child->trigger->path;
|
||||||
|
|
||||||
|
count = config_fmt_to_files(trfile_fmt, &trfiles);
|
||||||
|
if (count <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
res = read_file(trfiles[i], &size, &content);
|
||||||
|
if (res == -1) {
|
||||||
|
LOGE("read %s failed, error (%s)\n",
|
||||||
|
trfiles[i], strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (crash_match_content(child, content)) {
|
||||||
|
free(content);
|
||||||
|
matched_child = child;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
free(trfiles[i]);
|
||||||
|
free(trfiles);
|
||||||
|
|
||||||
|
if (matched_child)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It returns the first matched crash */
|
||||||
|
return matched_child;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Judge the crash type. We only got a root crash from channel, sometimes,
|
* Judge the crash type. We only got a root crash from channel, sometimes,
|
||||||
* we need to calculate a more specific type.
|
* we need to calculate a more specific type.
|
||||||
@ -208,7 +265,7 @@ fail:
|
|||||||
* This function couldn't use for binary file.
|
* This function couldn't use for binary file.
|
||||||
*
|
*
|
||||||
* @param rcrash Root crash obtained from channel.
|
* @param rcrash Root crash obtained from channel.
|
||||||
* @param trfile Path of trigger file.
|
* @param rtrfile_fmt Path fmt of trigger file of root crash.
|
||||||
* @param[out] data0 Searched result, according to 'data0' configuread in crash.
|
* @param[out] data0 Searched result, according to 'data0' configuread in crash.
|
||||||
* @param[out] data1 Searched result, according to 'data1' configuread in crash.
|
* @param[out] data1 Searched result, according to 'data1' configuread in crash.
|
||||||
* @param[out] data2 Searched result, according to 'data2' configuread in crash.
|
* @param[out] data2 Searched result, according to 'data2' configuread in crash.
|
||||||
@ -216,64 +273,64 @@ fail:
|
|||||||
* @return a pointer to the calculated crash structure if successful,
|
* @return a pointer to the calculated crash structure if successful,
|
||||||
* or NULL if not.
|
* or NULL if not.
|
||||||
*/
|
*/
|
||||||
static struct crash_t *crash_reclassify_by_content(struct crash_t *rcrash,
|
static struct crash_t *crash_reclassify_by_content(const struct crash_t *rcrash,
|
||||||
char *trfile, char **data0,
|
const char *rtrfile_fmt, char **data0,
|
||||||
char **data1, char **data2)
|
char **data1, char **data2)
|
||||||
{
|
{
|
||||||
int depth;
|
int count;
|
||||||
int level;
|
const struct crash_t *crash;
|
||||||
int ret;
|
const struct crash_t *ret_crash = rcrash;
|
||||||
struct crash_t *crash;
|
const char *trfile_fmt;
|
||||||
struct crash_t *ret_crash = NULL;
|
char **trfiles;
|
||||||
void *file;
|
void *content;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
int id;
|
int res;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!rcrash)
|
if (!rcrash)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (trfile) {
|
crash = rcrash;
|
||||||
ret = read_file(trfile, &size, &file);
|
|
||||||
if (ret == -1) {
|
|
||||||
LOGE("read %s failed, error (%s)\n",
|
|
||||||
trfile, strerror(errno));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
return rcrash;
|
|
||||||
|
|
||||||
/* traverse every crash from leaf, return the first crash we find
|
while (1) {
|
||||||
* consider that we have few CRASH TYPE, so just using this simple
|
crash = crash_find_matched_child(crash, rtrfile_fmt);
|
||||||
* implementation.
|
if (!crash)
|
||||||
*/
|
break;
|
||||||
depth = crash_depth(rcrash);
|
|
||||||
for (level = depth; level >= 0; level--) {
|
|
||||||
for_each_crash(id, crash, conf) {
|
|
||||||
if (!crash ||
|
|
||||||
crash->trigger != rcrash->trigger ||
|
|
||||||
crash->channel != rcrash->channel ||
|
|
||||||
crash->level != level)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (crash_reclassify(crash, file)) {
|
ret_crash = crash;
|
||||||
ret = get_data(file, crash, data0, data1,
|
|
||||||
data2);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOGE("get data error, error (%s)\n",
|
|
||||||
strerror(-ret));
|
|
||||||
goto fail_data;
|
|
||||||
} else {
|
|
||||||
ret_crash = crash;
|
|
||||||
goto fail_data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fail_data:
|
if (!strcmp(ret_crash->trigger->type, "dir"))
|
||||||
free(file);
|
trfile_fmt = rtrfile_fmt;
|
||||||
|
else
|
||||||
|
trfile_fmt = ret_crash->trigger->path;
|
||||||
|
|
||||||
return ret_crash;
|
count = config_fmt_to_files(trfile_fmt, &trfiles);
|
||||||
|
if (count <= 0)
|
||||||
|
return (struct crash_t *)ret_crash;
|
||||||
|
|
||||||
|
/* get data from last file */
|
||||||
|
res = read_file(trfiles[count - 1], &size, &content);
|
||||||
|
if (res == -1) {
|
||||||
|
LOGE("read %s failed, error (%s)\n",
|
||||||
|
trfiles[count - 1], strerror(errno));
|
||||||
|
goto free_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = get_data(content, ret_crash, data0, data1, data2);
|
||||||
|
if (res < 0) {
|
||||||
|
LOGE("get data error, error (%s)\n",
|
||||||
|
strerror(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(content);
|
||||||
|
|
||||||
|
free_files:
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
free(trfiles[i]);
|
||||||
|
free(trfiles);
|
||||||
|
|
||||||
|
return (struct crash_t *)ret_crash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -286,7 +343,7 @@ void init_crash_reclassify(void)
|
|||||||
struct crash_t *crash;
|
struct crash_t *crash;
|
||||||
|
|
||||||
for_each_crash(id, crash, conf) {
|
for_each_crash(id, crash, conf) {
|
||||||
if (!crash || !is_root_crash(crash))
|
if (!crash)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
crash->reclassify = crash_reclassify_by_content;
|
crash->reclassify = crash_reclassify_by_content;
|
||||||
|
@ -118,7 +118,7 @@ void hist_raise_event(char *event, char *type, char *log, char *lastuptime,
|
|||||||
char *key)
|
char *key)
|
||||||
{
|
{
|
||||||
char line[MAXLINESIZE];
|
char line[MAXLINESIZE];
|
||||||
char eventtime[32];
|
char eventtime[LONG_TIME_SIZE];
|
||||||
struct sender_t *crashlog;
|
struct sender_t *crashlog;
|
||||||
int maxlines;
|
int maxlines;
|
||||||
int ret;
|
int ret;
|
||||||
@ -158,7 +158,7 @@ void hist_raise_event(char *event, char *type, char *log, char *lastuptime,
|
|||||||
|
|
||||||
void hist_raise_uptime(char *lastuptime)
|
void hist_raise_uptime(char *lastuptime)
|
||||||
{
|
{
|
||||||
char boot_time[24];
|
char boot_time[UPTIME_SIZE];
|
||||||
char firstline[MAXLINESIZE];
|
char firstline[MAXLINESIZE];
|
||||||
int hours;
|
int hours;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -65,8 +65,8 @@ struct crash_t {
|
|||||||
|
|
||||||
int wd;
|
int wd;
|
||||||
int level;
|
int level;
|
||||||
struct crash_t *(*reclassify)(struct crash_t *, char*, char**, char**,
|
struct crash_t *(*reclassify)(const struct crash_t *, const char*,
|
||||||
char**);
|
char**, char**, char**);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct info_t {
|
struct info_t {
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
#ifndef __PROBEUTILS_H__
|
#ifndef __PROBEUTILS_H__
|
||||||
#define __PROBEUTILS_H__
|
#define __PROBEUTILS_H__
|
||||||
|
|
||||||
|
#define UPTIME_SIZE 24
|
||||||
|
#define LONG_TIME_SIZE 32
|
||||||
|
|
||||||
enum e_dir_mode {
|
enum e_dir_mode {
|
||||||
MODE_CRASH = 0,
|
MODE_CRASH = 0,
|
||||||
MODE_STATS,
|
MODE_STATS,
|
||||||
|
@ -50,7 +50,7 @@ unsigned long long get_uptime(void)
|
|||||||
return time_ns;
|
return time_ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_uptime_string(char newuptime[24], int *hours)
|
int get_uptime_string(char *newuptime, int *hours)
|
||||||
{
|
{
|
||||||
long long tm;
|
long long tm;
|
||||||
int seconds, minutes;
|
int seconds, minutes;
|
||||||
@ -68,11 +68,11 @@ int get_uptime_string(char newuptime[24], int *hours)
|
|||||||
/* hours */
|
/* hours */
|
||||||
*hours /= 60;
|
*hours /= 60;
|
||||||
|
|
||||||
return snprintf(newuptime, 24, "%04d:%02d:%02d", *hours,
|
return snprintf(newuptime, UPTIME_SIZE, "%04d:%02d:%02d", *hours,
|
||||||
minutes, seconds);
|
minutes, seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_current_time_long(char buf[32])
|
int get_current_time_long(char *buf)
|
||||||
{
|
{
|
||||||
time_t t;
|
time_t t;
|
||||||
struct tm *time_val;
|
struct tm *time_val;
|
||||||
@ -82,7 +82,7 @@ int get_current_time_long(char buf[32])
|
|||||||
if (!time_val)
|
if (!time_val)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return strftime(buf, 32, "%Y-%m-%d/%H:%M:%S ", time_val);
|
return strftime(buf, LONG_TIME_SIZE, "%Y-%m-%d/%H:%M:%S ", time_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -348,16 +348,18 @@ void generate_crashfile(char *dir, char *event, char *hashkey,
|
|||||||
{
|
{
|
||||||
char *buf;
|
char *buf;
|
||||||
char *path;
|
char *path;
|
||||||
char datetime[32];
|
char datetime[LONG_TIME_SIZE];
|
||||||
char uptime[32];
|
char uptime[UPTIME_SIZE];
|
||||||
int hours;
|
int hours;
|
||||||
int ret;
|
int ret;
|
||||||
const int fmtsize = 128;
|
const int fmtsize = 128;
|
||||||
int filesize;
|
int filesize;
|
||||||
|
|
||||||
|
datetime[0] = 0;
|
||||||
ret = get_current_time_long(datetime);
|
ret = get_current_time_long(datetime);
|
||||||
if (ret <= 0)
|
if (ret <= 0)
|
||||||
return;
|
return;
|
||||||
|
uptime[0] = 0;
|
||||||
get_uptime_string(uptime, &hours);
|
get_uptime_string(uptime, &hours);
|
||||||
|
|
||||||
filesize = fmtsize + strlen(event) +
|
filesize = fmtsize + strlen(event) +
|
||||||
|
@ -38,57 +38,62 @@ struct telemd_data_t {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* get_log_file_* only used to copy regular file which can be mmaped */
|
static int cal_log_filepath(char **out, const struct log_t *log,
|
||||||
static void get_log_file_complete(struct log_t *log, char *desdir)
|
const char *srcname, const char *desdir)
|
||||||
{
|
{
|
||||||
char *des;
|
const char *filename;
|
||||||
char *name;
|
int need_timestamp = 0;
|
||||||
int ret;
|
int hours;
|
||||||
|
char timebuf[UPTIME_SIZE];
|
||||||
|
|
||||||
name = log->name;
|
if (!out || !log || !desdir)
|
||||||
|
return -1;
|
||||||
|
|
||||||
ret = asprintf(&des, "%s/%s", desdir, name);
|
if (is_ac_filefmt(log->path))
|
||||||
if (ret < 0) {
|
filename = srcname;
|
||||||
LOGE("compute string failed, out of memory\n");
|
else
|
||||||
return;
|
filename = log->name;
|
||||||
|
|
||||||
|
if (!filename)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!strcmp(log->type, "cmd") || log->lines)
|
||||||
|
need_timestamp = 1;
|
||||||
|
|
||||||
|
if (need_timestamp) {
|
||||||
|
timebuf[0] = 0;
|
||||||
|
get_uptime_string(timebuf, &hours);
|
||||||
|
return asprintf(out, "%s/%s_%s", desdir, filename, timebuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = do_copy_tail(log->path, des, 0);
|
return asprintf(out, "%s/%s", desdir, filename);
|
||||||
if (ret < 0) {
|
|
||||||
LOGE("copy (%s) failed\n, error (%s)\n", log->path,
|
|
||||||
strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
free(des);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_log_file_tail(struct log_t *log, char *desdir)
|
/* get_log_file_* only used to copy regular file which can be mmaped */
|
||||||
|
static void get_log_file_complete(const char *despath, const char *srcpath)
|
||||||
|
{
|
||||||
|
const int ret = do_copy_tail(srcpath, despath, 0);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
LOGE("copy (%s) failed, error (%s)\n", srcpath,
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_log_file_tail(const char *despath, const char *srcpath,
|
||||||
|
const int lines)
|
||||||
{
|
{
|
||||||
char *des;
|
|
||||||
char timebuf[24];
|
|
||||||
char *name;
|
|
||||||
char *start;
|
char *start;
|
||||||
int lines;
|
|
||||||
int hours;
|
|
||||||
int start_line;
|
int start_line;
|
||||||
int file_lines;
|
int file_lines;
|
||||||
struct mm_file_t *mfile;
|
struct mm_file_t *mfile;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
lines = atoi(log->lines);
|
mfile = mmap_file(srcpath);
|
||||||
name = log->name;
|
|
||||||
get_uptime_string(timebuf, &hours);
|
|
||||||
ret = asprintf(&des, "%s/%s_%s", desdir, name, timebuf);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOGE("compute string failed, out of memory\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mfile = mmap_file(log->path);
|
|
||||||
if (!mfile) {
|
if (!mfile) {
|
||||||
LOGE("mmap (%s) failed, error (%s)\n", log->path,
|
LOGE("mmap (%s) failed, error (%s)\n", srcpath,
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
goto free;
|
return;
|
||||||
}
|
}
|
||||||
file_lines = mm_count_lines(mfile);
|
file_lines = mm_count_lines(mfile);
|
||||||
if (file_lines <= 0) {
|
if (file_lines <= 0) {
|
||||||
@ -97,184 +102,65 @@ static void get_log_file_tail(struct log_t *log, char *desdir)
|
|||||||
}
|
}
|
||||||
start_line = MAX(file_lines - lines, 0) + 1;
|
start_line = MAX(file_lines - lines, 0) + 1;
|
||||||
start = mm_get_line(mfile, start_line);
|
start = mm_get_line(mfile, start_line);
|
||||||
ret = overwrite_file(des, start);
|
ret = overwrite_file(despath, start);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOGE("create file with (%s, %p) failed, error (%s)\n",
|
LOGE("create file with (%s, %p) failed, error (%s)\n",
|
||||||
des, start, strerror(errno));
|
despath, start, strerror(errno));
|
||||||
goto unmap;
|
goto unmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
unmap:
|
unmap:
|
||||||
unmap_file(mfile);
|
unmap_file(mfile);
|
||||||
free:
|
|
||||||
free(des);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_log_file(struct log_t *log, char *desdir)
|
static void get_log_file(const char *despath, const char *srcpath,
|
||||||
|
const char *tail_lines)
|
||||||
{
|
{
|
||||||
int lines;
|
int lines;
|
||||||
|
|
||||||
if (log->lines == NULL) {
|
if (!tail_lines) {
|
||||||
get_log_file_complete(log, desdir);
|
get_log_file_complete(despath, srcpath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = atoi(log->lines);
|
lines = atoi(tail_lines);
|
||||||
if (lines > 0)
|
if (lines > 0)
|
||||||
get_log_file_tail(log, desdir);
|
get_log_file_tail(despath, srcpath, lines);
|
||||||
else
|
else
|
||||||
get_log_file_complete(log, desdir);
|
get_log_file_complete(despath, srcpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_log_rotation(struct log_t *log, char *desdir)
|
static void get_log_node(const char *despath, const char *nodepath)
|
||||||
{
|
{
|
||||||
char *suffix;
|
const int res = do_copy_eof(nodepath, despath);
|
||||||
char *prefix;
|
|
||||||
char *dir;
|
|
||||||
char *p;
|
|
||||||
int count;
|
|
||||||
char *files[512];
|
|
||||||
int number;
|
|
||||||
int target_num = -1;
|
|
||||||
char *target_file = NULL;
|
|
||||||
char *name;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
dir = strdup(log->path);
|
if (res < 0) {
|
||||||
if (!dir) {
|
LOGE("copy (%s) failed, error (%s)\n", nodepath,
|
||||||
LOGE("compute string failed, out of memory\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* dir prefix suffix
|
|
||||||
* | | |
|
|
||||||
* /tmp/hvlog/hvlog_cur.[biggest]
|
|
||||||
*/
|
|
||||||
p = strrchr(dir, '/');
|
|
||||||
if (p == NULL) {
|
|
||||||
LOGE("invalid path (%s) in log (%s), ", dir, log->name);
|
|
||||||
LOGE("file_rotation only support absolute path\n");
|
|
||||||
goto free_dir;
|
|
||||||
} else {
|
|
||||||
prefix = p + 1;
|
|
||||||
*p = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = strstr(prefix, ".[");
|
|
||||||
if (p == NULL) {
|
|
||||||
LOGE("invalid path (%s) in log (%s)\n", log->path, log->name);
|
|
||||||
goto free_dir;
|
|
||||||
} else {
|
|
||||||
suffix = p + 2;
|
|
||||||
*p = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = suffix + strlen(suffix) - 1;
|
|
||||||
if (*p == ']') {
|
|
||||||
*p = 0;
|
|
||||||
} else {
|
|
||||||
LOGE("invalid path (%s) in log (%s)\n", log->path, log->name);
|
|
||||||
goto free_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct log_t toget;
|
|
||||||
|
|
||||||
memcpy(&toget, log, sizeof(toget));
|
|
||||||
count = lsdir(dir, files, ARRAY_SIZE(files));
|
|
||||||
if (count > 2) {
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
name = strrchr(files[i], '/') + 1;
|
|
||||||
if (!name && !strstr(name, prefix))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
number = atoi(strrchr(name, '.') + 1);
|
|
||||||
if (!strncmp(suffix, "biggest", 7)) {
|
|
||||||
if (target_num == -1 ||
|
|
||||||
number > target_num){
|
|
||||||
target_file = files[i];
|
|
||||||
target_num = number;
|
|
||||||
}
|
|
||||||
} else if (!strncmp(suffix, "smallest", 8)) {
|
|
||||||
if (target_num == -1 ||
|
|
||||||
number < target_num) {
|
|
||||||
target_file = files[i];
|
|
||||||
target_num = number;
|
|
||||||
}
|
|
||||||
} else if (!strncmp(suffix, "all", 3)) {
|
|
||||||
toget.path = files[i];
|
|
||||||
toget.name = name;
|
|
||||||
get_log_file(&toget, desdir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (count < 0) {
|
|
||||||
LOGE("lsdir (%s) failed, error (%s)\n", dir,
|
|
||||||
strerror(-count));
|
|
||||||
goto free;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strncmp(suffix, "all", 3))
|
|
||||||
goto free;
|
|
||||||
|
|
||||||
if (target_file) {
|
|
||||||
toget.path = target_file;
|
|
||||||
get_log_file(&toget, desdir);
|
|
||||||
} else {
|
|
||||||
LOGW("no logs found for (%s)\n", log->name);
|
|
||||||
goto free;
|
|
||||||
}
|
|
||||||
|
|
||||||
free:
|
|
||||||
while (count > 0)
|
|
||||||
free(files[--count]);
|
|
||||||
free_dir:
|
|
||||||
free(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void get_log_node(struct log_t *log, char *desdir)
|
|
||||||
{
|
|
||||||
char *des;
|
|
||||||
char *name;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
name = log->name;
|
|
||||||
ret = asprintf(&des, "%s/%s", desdir, name);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOGE("compute string failed, out of memory\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = do_copy_eof(log->path, des);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOGE("copy (%s) failed, error (%s)\n", log->path,
|
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
goto free;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free:
|
|
||||||
free(des);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void out_via_fork(struct log_t *log, char *desdir)
|
static void get_log_cmd(const char *despath, const char *cmd)
|
||||||
{
|
{
|
||||||
char *des;
|
const int res = exec_out2file(despath, cmd);
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = asprintf(&des, "%s/%s", desdir, log->name);
|
if (res)
|
||||||
if (ret < 0) {
|
LOGE("get_log_by_cmd exec %s returns (%d)\n", cmd, res);
|
||||||
LOGE("compute string failed, out of memory\n");
|
}
|
||||||
|
|
||||||
|
static void get_log_by_type(const char *despath, const struct log_t *log,
|
||||||
|
const char *srcpath)
|
||||||
|
{
|
||||||
|
if (!despath || !log || !srcpath)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
exec_out2file(des, log->path);
|
if (!strcmp("file", log->type))
|
||||||
free(des);
|
get_log_file(despath, srcpath, log->lines);
|
||||||
|
else if (!strcmp("node", log->type))
|
||||||
|
get_log_node(despath, log->path);
|
||||||
|
else if (!strcmp("cmd", log->type))
|
||||||
|
get_log_cmd(despath, log->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_log_cmd(struct log_t *log, char *desdir)
|
|
||||||
{
|
|
||||||
out_via_fork(log, desdir);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_TELEMETRICS_CLIENT
|
#ifdef HAVE_TELEMETRICS_CLIENT
|
||||||
static int telemd_send_data(char *payload, char *eventid, uint32_t severity,
|
static int telemd_send_data(char *payload, char *eventid, uint32_t severity,
|
||||||
char *class)
|
char *class)
|
||||||
@ -322,70 +208,47 @@ fail:
|
|||||||
|
|
||||||
static void telemd_get_log(struct log_t *log, void *data)
|
static void telemd_get_log(struct log_t *log, void *data)
|
||||||
{
|
{
|
||||||
struct telemd_data_t *d = (struct telemd_data_t *)data;
|
const struct telemd_data_t *d = (struct telemd_data_t *)data;
|
||||||
char name[NAME_MAX];
|
char fpath[PATH_MAX];
|
||||||
char *path, *msg;
|
char *msg;
|
||||||
int ret;
|
int count;
|
||||||
|
int res;
|
||||||
|
int i;
|
||||||
|
struct dirent **filelist;
|
||||||
|
|
||||||
if (d->srcdir == NULL)
|
if (d->srcdir == NULL)
|
||||||
goto send_nologs;
|
goto send_nologs;
|
||||||
|
|
||||||
ret = dir_contains(d->srcdir, log->name, 0, name);
|
/* search file which use log->name as substring */
|
||||||
if (ret == 1) {
|
count = ac_scandir(d->srcdir, &filelist, filter_filename_substr,
|
||||||
ret = asprintf(&path, "%s/%s", d->srcdir, name);
|
log->name, NULL);
|
||||||
if (ret < 0) {
|
if (count < 0) {
|
||||||
LOGE("compute string failed, out of memory\n");
|
LOGE("search (%s) in dir (%s) failed, error (%s)\n", log->name,
|
||||||
return;
|
d->srcdir, strerror(count));
|
||||||
}
|
return;
|
||||||
telemd_send_data(path, d->eventid, d->severity, d->class);
|
}
|
||||||
free(path);
|
if (!count) {
|
||||||
} else if (ret == 0) {
|
|
||||||
LOGE("dir (%s) does not contains (%s)\n", d->srcdir,
|
LOGE("dir (%s) does not contains (%s)\n", d->srcdir,
|
||||||
log->name);
|
log->name);
|
||||||
goto send_nologs;
|
goto send_nologs;
|
||||||
} else if (ret > 1) {
|
|
||||||
/* got multiple files */
|
|
||||||
int i;
|
|
||||||
int count;
|
|
||||||
char *name;
|
|
||||||
char *files[512];
|
|
||||||
|
|
||||||
if (!strstr(log->path, ".[all]")) {
|
|
||||||
LOGE("dir (%s) contains (%d) files (%s)\n",
|
|
||||||
d->srcdir, ret, log->name);
|
|
||||||
goto send_nologs;
|
|
||||||
}
|
|
||||||
|
|
||||||
count = lsdir(d->srcdir, files, ARRAY_SIZE(files));
|
|
||||||
if (count > 2) {
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
name = strrchr(files[i], '/') + 1;
|
|
||||||
if (!strstr(name, log->name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
telemd_send_data(files[i], d->eventid,
|
|
||||||
d->severity, d->class);
|
|
||||||
}
|
|
||||||
} else if (count < 0) {
|
|
||||||
LOGE("lsdir (%s) failed, error (%s)\n", d->srcdir,
|
|
||||||
strerror(-count));
|
|
||||||
goto send_nologs;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (count > 0)
|
|
||||||
free(files[--count]);
|
|
||||||
} else {
|
|
||||||
LOGE("search (%s) in dir (%s) failed, error (%s)\n", log->name,
|
|
||||||
d->srcdir, strerror(-ret));
|
|
||||||
goto send_nologs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
snprintf(fpath, sizeof(fpath), "%s/%s", d->srcdir,
|
||||||
|
filelist[i]->d_name);
|
||||||
|
free(filelist[i]);
|
||||||
|
telemd_send_data(fpath, d->eventid,
|
||||||
|
d->severity, d->class);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(filelist);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
send_nologs:
|
send_nologs:
|
||||||
ret = asprintf(&msg, "no log generated on %s, check probe's log.",
|
res = asprintf(&msg, "no log generated on %s, check probe's log.",
|
||||||
log->name);
|
log->name);
|
||||||
if (ret < 0) {
|
if (res < 0) {
|
||||||
LOGE("compute string failed, out of memory\n");
|
LOGE("compute string failed, out of memory\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -402,6 +265,8 @@ static void crashlog_get_log(struct log_t *log, void *data)
|
|||||||
unsigned long long start, end;
|
unsigned long long start, end;
|
||||||
int spent;
|
int spent;
|
||||||
int quota;
|
int quota;
|
||||||
|
int res;
|
||||||
|
char *des;
|
||||||
char *desdir = (char *)data;
|
char *desdir = (char *)data;
|
||||||
|
|
||||||
crashlog = get_sender_by_name("crashlog");
|
crashlog = get_sender_by_name("crashlog");
|
||||||
@ -415,14 +280,53 @@ static void crashlog_get_log(struct log_t *log, void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
start = get_uptime();
|
start = get_uptime();
|
||||||
if (!strcmp("file", log->type))
|
if (is_ac_filefmt(log->path)) {
|
||||||
get_log_file(log, desdir);
|
int i;
|
||||||
else if (!strcmp("node", log->type))
|
char **files;
|
||||||
get_log_node(log, desdir);
|
char *name;
|
||||||
else if (!strcmp("cmd", log->type))
|
|
||||||
get_log_cmd(log, desdir);
|
const int count = config_fmt_to_files(log->path, &files);
|
||||||
else if (!strcmp("file_rotation", log->type))
|
|
||||||
get_log_rotation(log, desdir);
|
if (count < 0) {
|
||||||
|
LOGE("parse config format (%s) failed, error (%s)\n",
|
||||||
|
log->path, strerror(count));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!count) {
|
||||||
|
LOGW("no logs found for (%s)\n", log->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
name = strrchr(files[i], '/') + 1;
|
||||||
|
if (name == (char *)1) {
|
||||||
|
LOGE("invalid path (%s) in log (%s)", files[i],
|
||||||
|
log->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
res = cal_log_filepath(&des, log, name, desdir);
|
||||||
|
if (res == -1) {
|
||||||
|
LOGE("cal_log_filepath failed, error (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
get_log_by_type(des, log, files[i]);
|
||||||
|
free(des);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
free(files[i]);
|
||||||
|
free(files);
|
||||||
|
} else {
|
||||||
|
res = cal_log_filepath(&des, log, log->name, desdir);
|
||||||
|
if (res == -1) {
|
||||||
|
LOGE("cal_log_filepath failed, error (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
get_log_by_type(des, log, log->path);
|
||||||
|
free(des);
|
||||||
|
}
|
||||||
end = get_uptime();
|
end = get_uptime();
|
||||||
|
|
||||||
spent = (int)((end - start) / 1000000000LL);
|
spent = (int)((end - start) / 1000000000LL);
|
||||||
@ -560,7 +464,7 @@ static void telemd_send_uptime(void)
|
|||||||
struct sender_t *telemd;
|
struct sender_t *telemd;
|
||||||
struct uptime_t *uptime;
|
struct uptime_t *uptime;
|
||||||
char *class;
|
char *class;
|
||||||
char boot_time[24];
|
char boot_time[UPTIME_SIZE];
|
||||||
int hours;
|
int hours;
|
||||||
int ret;
|
int ret;
|
||||||
static int uptime_hours;
|
static int uptime_hours;
|
||||||
@ -812,22 +716,21 @@ static void crashlog_send_crash(struct event_t *e)
|
|||||||
struct sender_t *crashlog;
|
struct sender_t *crashlog;
|
||||||
char *key = NULL;
|
char *key = NULL;
|
||||||
char *trfile = NULL;
|
char *trfile = NULL;
|
||||||
char *data0;
|
char *data0 = NULL;
|
||||||
char *data1;
|
char *data1 = NULL;
|
||||||
char *data2;
|
char *data2 = NULL;
|
||||||
int id;
|
int id;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int quota;
|
int quota;
|
||||||
struct crash_t *rcrash = (struct crash_t *)e->private;
|
struct crash_t *rcrash = (struct crash_t *)e->private;
|
||||||
|
|
||||||
if (!strcmp(rcrash->trigger->type, "file"))
|
if (!strcmp(rcrash->trigger->type, "dir")) {
|
||||||
ret = asprintf(&trfile, "%s", rcrash->trigger->path);
|
|
||||||
else if (!strcmp(rcrash->trigger->type, "dir"))
|
|
||||||
ret = asprintf(&trfile, "%s/%s", rcrash->trigger->path,
|
ret = asprintf(&trfile, "%s/%s", rcrash->trigger->path,
|
||||||
e->path);
|
e->path);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOGE("compute string failed, out of memory\n");
|
LOGE("compute string failed, out of memory\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
crash = rcrash->reclassify(rcrash, trfile, &data0, &data1, &data2);
|
crash = rcrash->reclassify(rcrash, trfile, &data0, &data1, &data2);
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
* If all system calls succeed, then the return value is the
|
* If all system calls succeed, then the return value is the
|
||||||
* termination status of the child process used to execute command.
|
* termination status of the child process used to execute command.
|
||||||
*/
|
*/
|
||||||
int execv_out2file(char *argv[], char *outfile)
|
int execv_out2file(char * const argv[], const char *outfile)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
@ -103,13 +103,13 @@ int execv_out2file(char *argv[], char *outfile)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int debugfs_cmd(char *loop_dev, char *cmd, char *outfile)
|
int debugfs_cmd(const char *loop_dev, const char *cmd, const char *outfile)
|
||||||
{
|
{
|
||||||
char *argv[5] = {"debugfs", "-R", NULL, NULL, 0};
|
const char *argv[5] = {"debugfs", "-R", NULL, NULL, 0};
|
||||||
|
|
||||||
argv[2] = cmd;
|
argv[2] = cmd;
|
||||||
argv[3] = loop_dev;
|
argv[3] = loop_dev;
|
||||||
return execv_out2file(argv, outfile);
|
return execv_out2file((char * const *)argv, outfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,7 +128,7 @@ int debugfs_cmd(char *loop_dev, char *cmd, char *outfile)
|
|||||||
* If all system calls succeed, then the return value is the
|
* If all system calls succeed, then the return value is the
|
||||||
* termination status of the child process used to execute command.
|
* termination status of the child process used to execute command.
|
||||||
*/
|
*/
|
||||||
int exec_out2file(char *outfile, char *fmt, ...)
|
int exec_out2file(const char *outfile, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
char *cmd;
|
char *cmd;
|
||||||
@ -184,7 +184,7 @@ int exec_out2file(char *outfile, char *fmt, ...)
|
|||||||
*
|
*
|
||||||
* @return a pointer to command's output if successful, or NULL if not.
|
* @return a pointer to command's output if successful, or NULL if not.
|
||||||
*/
|
*/
|
||||||
char *exec_out2mem(char *fmt, ...)
|
char *exec_out2mem(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
char *cmd;
|
char *cmd;
|
||||||
|
@ -300,7 +300,7 @@ void unmap_file(struct mm_file_t *mfile)
|
|||||||
* @return The number of bytes written to new file if successful,
|
* @return The number of bytes written to new file if successful,
|
||||||
* or a negative errno-style value if not.
|
* or a negative errno-style value if not.
|
||||||
*/
|
*/
|
||||||
int do_copy_tail(char *src, char *dest, int limit)
|
int do_copy_tail(const char *src, const char *dest, int limit)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int fsrc = -1, fdest = -1;
|
int fsrc = -1, fdest = -1;
|
||||||
@ -916,41 +916,130 @@ int file_read_key_value_r(const char *path, const char *key,
|
|||||||
return _file_read_key_value(path, 'r', key, limit, value);
|
return _file_read_key_value(path, 'r', key, limit, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dir_contains(const char *dir, const char *filename, int exact,
|
/**
|
||||||
char *fullname)
|
* Because scandir's filter can't receive caller's parameter, so
|
||||||
|
* rewrite an ac_scandir to satisfy our usage. This function is
|
||||||
|
* very like scandir, except it has an additional parameter farg for
|
||||||
|
* filter.
|
||||||
|
*
|
||||||
|
* @param dirp Dir to scan.
|
||||||
|
* @param namelist Andress to receive result array of struct dirent.
|
||||||
|
* @param filter Function pointer to filter. See also scandir.
|
||||||
|
* @param farg The second arg of filter.
|
||||||
|
* @param compar See scandir.
|
||||||
|
*
|
||||||
|
* @return the count of scanned files if successful, or a negative
|
||||||
|
* errno-style value if not.
|
||||||
|
*/
|
||||||
|
int ac_scandir(const char *dirp, struct dirent ***namelist,
|
||||||
|
int (*filter)(const struct dirent *, const void *),
|
||||||
|
const void *farg,
|
||||||
|
int (*compar)(const struct dirent **,
|
||||||
|
const struct dirent **))
|
||||||
{
|
{
|
||||||
int ret, count = 0;
|
int i;
|
||||||
|
int count = 0;
|
||||||
|
int index = 0;
|
||||||
|
struct dirent **_filelist;
|
||||||
|
|
||||||
|
if (!dirp || !namelist)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
const int res = scandir(dirp, &_filelist, NULL, compar);
|
||||||
|
|
||||||
|
if (!filter) {
|
||||||
|
*namelist = _filelist;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (res == -1)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
/* overwrite filter */
|
||||||
|
/* calculate the matched files, free unneeded files and mark them */
|
||||||
|
for (i = 0; i < res; i++) {
|
||||||
|
if (!filter(_filelist[i], farg)) {
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
free(_filelist[i]);
|
||||||
|
_filelist[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no matched result */
|
||||||
|
if (!count) {
|
||||||
|
free(_filelist);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* construct the out array */
|
||||||
|
*namelist = malloc(count * sizeof(struct dirent *));
|
||||||
|
if (!(*namelist))
|
||||||
|
goto e_free;
|
||||||
|
|
||||||
|
for (i = 0; i < res; i++) {
|
||||||
|
if (_filelist[i])
|
||||||
|
(*namelist)[index++] = _filelist[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
free(_filelist);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
|
||||||
|
e_free:
|
||||||
|
for (i = 0; i < res; i++)
|
||||||
|
if (_filelist[i])
|
||||||
|
free(_filelist[i]);
|
||||||
|
free(_filelist);
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* filters return zero if the match is successful */
|
||||||
|
int filter_filename_substr(const struct dirent *entry, const void *arg)
|
||||||
|
{
|
||||||
|
const char *substr = (const char *)arg;
|
||||||
|
|
||||||
|
return !strstr(entry->d_name, substr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int filter_filename_exactly(const struct dirent *entry, const void *arg)
|
||||||
|
{
|
||||||
|
const char *fname = (const char *)arg;
|
||||||
|
|
||||||
|
return strcmp(entry->d_name, fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
int filter_filename_startswith(const struct dirent *entry,
|
||||||
|
const void *arg)
|
||||||
|
{
|
||||||
|
const char *str = (const char *)arg;
|
||||||
|
|
||||||
|
return memcmp(entry->d_name, str, strlen(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
int dir_contains(const char *dir, const char *filename, const int exact)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
struct dirent **filelist;
|
struct dirent **filelist;
|
||||||
char *name;
|
|
||||||
|
|
||||||
if (!dir || !filename)
|
if (!dir || !filename)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* pass parameters to scandir's filter is not convenience, so we use
|
if (exact)
|
||||||
* this implementation.
|
ret = ac_scandir(dir, &filelist, filter_filename_exactly,
|
||||||
*/
|
(const void *)filename, 0);
|
||||||
ret = scandir(dir, &filelist, 0, 0);
|
else
|
||||||
if (ret < 0)
|
ret = ac_scandir(dir, &filelist, filter_filename_substr,
|
||||||
return -errno;
|
(const void *)filename, 0);
|
||||||
|
if (ret <= 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
while (ret--) {
|
for (i = 0; i < ret; i++)
|
||||||
name = filelist[ret]->d_name;
|
free(filelist[i]);
|
||||||
if (exact) {
|
|
||||||
if (!strcmp(name, filename))
|
|
||||||
count++;
|
|
||||||
} else {
|
|
||||||
if (strstr(name, filename)) {
|
|
||||||
count++;
|
|
||||||
if (fullname)
|
|
||||||
strcpy(fullname, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(filelist[ret]);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(filelist);
|
free(filelist);
|
||||||
|
|
||||||
return count;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lsdir(const char *dir, char *fullname[], int limit)
|
int lsdir(const char *dir, char *fullname[], int limit)
|
||||||
@ -1087,7 +1176,7 @@ int find_file(char *dir, char *target_file, int depth, char *path[], int limit)
|
|||||||
if (count >= limit)
|
if (count >= limit)
|
||||||
goto free;
|
goto free;
|
||||||
|
|
||||||
ret = dir_contains(_dirs[i], target_file, 1, NULL);
|
ret = dir_contains(_dirs[i], target_file, 1);
|
||||||
if (ret == 1) {
|
if (ret == 1) {
|
||||||
ret = asprintf(&path[count++], "%s/%s",
|
ret = asprintf(&path[count++], "%s/%s",
|
||||||
_dirs[i], target_file);
|
_dirs[i], target_file);
|
||||||
@ -1096,9 +1185,6 @@ int find_file(char *dir, char *target_file, int depth, char *path[], int limit)
|
|||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
} else if (ret > 1) {
|
|
||||||
LOGE("found (%d) (%s) under (%s)??\n",
|
|
||||||
ret, target_file, dir);
|
|
||||||
} else if (ret < 0) {
|
} else if (ret < 0) {
|
||||||
LOGE("dir_contains failed, error (%s)\n",
|
LOGE("dir_contains failed, error (%s)\n",
|
||||||
strerror(-ret));
|
strerror(-ret));
|
||||||
@ -1171,3 +1257,140 @@ free:
|
|||||||
close(fd);
|
close(fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int is_ac_filefmt(const char *file_fmt)
|
||||||
|
{
|
||||||
|
/* Supported formats:
|
||||||
|
* - /dir/.../file[*] --> all files with prefix "file."
|
||||||
|
* - /dir/.../file[0] --> file with smallest subfix num.
|
||||||
|
* - /dir/.../file[-1] --> file with biggest subfix num.
|
||||||
|
*/
|
||||||
|
if (!file_fmt)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (strstr(file_fmt, "[*]") ||
|
||||||
|
strstr(file_fmt, "[0]") ||
|
||||||
|
strstr(file_fmt, "[-1]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The config file of acrnprobe could use some format to indicate a file/files.
|
||||||
|
* This function is used to parse the format and returns found files paths.
|
||||||
|
*
|
||||||
|
* @param file_fmt A string pointer of a file format.
|
||||||
|
* @param out Files were found.
|
||||||
|
*
|
||||||
|
* @return the count of searched files if successful, or a negative
|
||||||
|
* errno-style value if not.
|
||||||
|
*/
|
||||||
|
int config_fmt_to_files(const char *file_fmt, char ***out)
|
||||||
|
{
|
||||||
|
char type[3];
|
||||||
|
char *dir;
|
||||||
|
char *p;
|
||||||
|
char *subfix;
|
||||||
|
char *file_prefix;
|
||||||
|
int i;
|
||||||
|
int count;
|
||||||
|
int res;
|
||||||
|
int ret = 0;
|
||||||
|
struct dirent **filelist;
|
||||||
|
char **out_array;
|
||||||
|
|
||||||
|
if (!file_fmt || !out)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
dir = strdup(file_fmt);
|
||||||
|
if (!dir)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!is_ac_filefmt(file_fmt)) {
|
||||||
|
/* It's an regular file as default */
|
||||||
|
out_array = malloc(sizeof(char *));
|
||||||
|
if (!out_array) {
|
||||||
|
ret = -errno;
|
||||||
|
goto free_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_array[0] = dir;
|
||||||
|
*out = out_array;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get dir and file prefix from format */
|
||||||
|
p = strrchr(dir, '/');
|
||||||
|
if (!p) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto free_dir;
|
||||||
|
}
|
||||||
|
*p = '\0';
|
||||||
|
file_prefix = p + 1;
|
||||||
|
*strrchr(file_prefix, '[') = '\0';
|
||||||
|
|
||||||
|
if (!directory_exists(dir)) {
|
||||||
|
ret = 0;
|
||||||
|
goto free_dir;
|
||||||
|
}
|
||||||
|
/* get format type */
|
||||||
|
subfix = strrchr(file_fmt, '[');
|
||||||
|
res = sscanf(subfix, "[%2[01-*]]", type);
|
||||||
|
if (res != 1) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto free_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get all files which start with prefix */
|
||||||
|
count = ac_scandir(dir, &filelist, filter_filename_startswith,
|
||||||
|
file_prefix, alphasort);
|
||||||
|
if (count <= 0) {
|
||||||
|
ret = count;
|
||||||
|
goto free_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* construct output */
|
||||||
|
out_array = (char **)malloc(count * sizeof(char *));
|
||||||
|
if (!out_array) {
|
||||||
|
ret = -errno;
|
||||||
|
goto free_filelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(type, "*")) {
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
res = asprintf(&out_array[i], "%s/%s", dir,
|
||||||
|
filelist[i]->d_name);
|
||||||
|
if (res == -1) {
|
||||||
|
/* free from 0 to i -1 */
|
||||||
|
while (i)
|
||||||
|
free(out_array[--i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = count;
|
||||||
|
} else if (!strcmp(type, "0")) {
|
||||||
|
res = asprintf(&out_array[0], "%s/%s", dir,
|
||||||
|
filelist[0]->d_name);
|
||||||
|
ret = 1;
|
||||||
|
} else if (!strcmp(type, "-1")) {
|
||||||
|
res = asprintf(&out_array[0], "%s/%s", dir,
|
||||||
|
filelist[count - 1]->d_name);
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* error happends while constructing output */
|
||||||
|
if (res == -1) {
|
||||||
|
ret = -errno;
|
||||||
|
free(out_array);
|
||||||
|
goto free_filelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = out_array;
|
||||||
|
|
||||||
|
free_filelist:
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
free(filelist[i]);
|
||||||
|
free(filelist);
|
||||||
|
free_dir:
|
||||||
|
free(dir);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* SPDX-License-Identifier: BSD-3-Clause
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int execv_out2file(char *argv[], char *outfile);
|
int execv_out2file(char * const argv[], const char *outfile);
|
||||||
int debugfs_cmd(char *loop_dev, char *cmd, char *outfile);
|
int debugfs_cmd(const char *loop_dev, const char *cmd, const char *outfile);
|
||||||
int exec_out2file(char *outfile, char *fmt, ...);
|
int exec_out2file(const char *outfile, const char *fmt, ...);
|
||||||
char *exec_out2mem(char *fmt, ...);
|
char *exec_out2mem(const char *fmt, ...);
|
||||||
|
@ -75,7 +75,7 @@ int mkdir_p(char *path);
|
|||||||
int mm_count_lines(struct mm_file_t *mfile);
|
int mm_count_lines(struct mm_file_t *mfile);
|
||||||
struct mm_file_t *mmap_file(const char *path);
|
struct mm_file_t *mmap_file(const char *path);
|
||||||
void unmap_file(struct mm_file_t *mfile);
|
void unmap_file(struct mm_file_t *mfile);
|
||||||
int do_copy_tail(char *src, char *dest, int limit);
|
int do_copy_tail(const char *src, const char *dest, int limit);
|
||||||
int do_mv(char *src, char *dest);
|
int do_mv(char *src, char *dest);
|
||||||
int append_file(char *filename, char *text);
|
int append_file(char *filename, char *text);
|
||||||
int mm_replace_str_line(struct mm_file_t *mfile, char *replace,
|
int mm_replace_str_line(struct mm_file_t *mfile, char *replace,
|
||||||
@ -97,10 +97,20 @@ int file_read_key_value(const char *path, const char *key,
|
|||||||
const size_t limit, char *value);
|
const size_t limit, char *value);
|
||||||
int file_read_key_value_r(const char *path, const char *key,
|
int file_read_key_value_r(const char *path, const char *key,
|
||||||
const size_t limit, char *value);
|
const size_t limit, char *value);
|
||||||
int dir_contains(const char *dir, const char *filename, int exact,
|
int ac_scandir(const char *dirp, struct dirent ***namelist,
|
||||||
char *fullname);
|
int (*filter)(const struct dirent *, const void *),
|
||||||
|
const void *farg,
|
||||||
|
int (*compar)(const struct dirent **,
|
||||||
|
const struct dirent **));
|
||||||
|
int filter_filename_substr(const struct dirent *entry, const void *arg);
|
||||||
|
int filter_filename_exactly(const struct dirent *entry, const void *arg);
|
||||||
|
int filter_filename_startswith(const struct dirent *entry,
|
||||||
|
const void *arg);
|
||||||
|
int dir_contains(const char *dir, const char *filename, int exact);
|
||||||
int lsdir(const char *dir, char *fullname[], int limit);
|
int lsdir(const char *dir, char *fullname[], int limit);
|
||||||
int find_file(char *dir, char *target_file, int depth, char *path[], int limit);
|
int find_file(char *dir, char *target_file, int depth, char *path[], int limit);
|
||||||
int read_file(const char *path, unsigned long *size, void **data);
|
int read_file(const char *path, unsigned long *size, void **data);
|
||||||
|
int is_ac_filefmt(const char *file_fmt);
|
||||||
|
int config_fmt_to_files(const char *file_fmt, char ***out);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -77,14 +77,14 @@
|
|||||||
</log>
|
</log>
|
||||||
<log id='5' enable='true'>
|
<log id='5' enable='true'>
|
||||||
<name>acrnlog_cur</name>
|
<name>acrnlog_cur</name>
|
||||||
<type>file_rotation</type>
|
<type>file</type>
|
||||||
<path>/tmp/acrnlog/acrnlog_cur.[biggest]</path>
|
<path>/tmp/acrnlog/acrnlog_cur.[-1]</path>
|
||||||
<lines>500</lines>
|
<lines>500</lines>
|
||||||
</log>
|
</log>
|
||||||
<log id='6' enable='true'>
|
<log id='6' enable='true'>
|
||||||
<name>acrnlog_last</name>
|
<name>acrnlog_last</name>
|
||||||
<type>file_rotation</type>
|
<type>file</type>
|
||||||
<path>/tmp/acrnlog/acrnlog_last.[all]</path>
|
<path>/tmp/acrnlog/acrnlog_last.[*]</path>
|
||||||
</log>
|
</log>
|
||||||
</logs>
|
</logs>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user