static int line_range_search_cb(Dwarf_Die *sp_die, void *data) { struct dwarf_callback_param *param = data; struct line_finder *lf = param->data; struct line_range *lr = lf->lr; if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die))) return DWARF_CB_OK; if (dwarf_tag(sp_die) == DW_TAG_subprogram && die_compare_name(sp_die, lr->function)) { lf->fname = dwarf_decl_file(sp_die); dwarf_decl_line(sp_die, &lr->offset); pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); lf->lno_s = lr->offset + lr->start; if (lf->lno_s < 0) lf->lno_s = INT_MAX; lf->lno_e = lr->offset + lr->end; if (lf->lno_e < 0) lf->lno_e = INT_MAX; pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); lr->start = lf->lno_s; lr->end = lf->lno_e; if (dwarf_func_inline(sp_die)) param->retval = die_walk_instances(sp_die, line_range_inline_cb, lf); else param->retval = find_line_range_by_line(sp_die, lf); return DWARF_CB_ABORT; } return DWARF_CB_OK; }
static int probe_point_lazy_walker(const char *fname, int lineno, Dwarf_Addr addr, void *data) { struct probe_finder *pf = data; Dwarf_Die *sc_die, die_mem; int ret; if (!line_list__has_line(&pf->lcache, lineno) || strtailcmp(fname, pf->fname) != 0) return 0; pr_debug("Probe line found: line:%d addr:0x%llx\n", lineno, (unsigned long long)addr); pf->addr = addr; pf->lno = lineno; sc_die = find_best_scope(pf, &die_mem); if (!sc_die) { pr_warning("Failed to find scope of probe point.\n"); return -ENOENT; } ret = call_probe_finder(sc_die, pf); return ret < 0 ? ret : 0; }
static int line_range_walk_cb(const char *fname, int lineno, Dwarf_Addr addr __used, void *data) { struct line_finder *lf = data; if ((strtailcmp(fname, lf->fname) != 0) || (lf->lno_s > lineno || lf->lno_e < lineno)) return 0; if (line_range_add_line(fname, lineno, lf->lr) < 0) return -EINVAL; return 0; }
static int probe_point_line_walker(const char *fname, int lineno, Dwarf_Addr addr, void *data) { struct probe_finder *pf = data; int ret; if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) return 0; pf->addr = addr; ret = call_probe_finder(NULL, pf); /* Continue if no error, because the line will be in inline function */ return ret < 0 ? ret : 0; }
static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) { struct dwarf_callback_param *param = data; struct probe_finder *pf = param->data; struct perf_probe_point *pp = &pf->pev->point; Dwarf_Attribute attr; if (dwarf_tag(sp_die) != DW_TAG_subprogram || !die_compare_name(sp_die, pp->function) || dwarf_attr(sp_die, DW_AT_declaration, &attr)) return DWARF_CB_OK; if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die))) return DWARF_CB_OK; pf->fname = dwarf_decl_file(sp_die); if (pp->line) { dwarf_decl_line(sp_die, &pf->lno); pf->lno += pp->line; param->retval = find_probe_point_by_line(pf); } else if (!dwarf_func_inline(sp_die)) { if (pp->lazy_line) param->retval = find_probe_point_lazy(sp_die, pf); else { if (dwarf_entrypc(sp_die, &pf->addr) != 0) { pr_warning("Failed to get entry address of " "%s.\n", dwarf_diename(sp_die)); param->retval = -ENOENT; return DWARF_CB_ABORT; } pf->addr += pp->offset; param->retval = call_probe_finder(sp_die, pf); } } else param->retval = die_walk_instances(sp_die, probe_point_inline_cb, (void *)pf); return DWARF_CB_ABORT; }
/* Search function from function name */ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) { struct dwarf_callback_param *param = data; struct probe_finder *pf = param->data; struct perf_probe_point *pp = &pf->pev->point; /* Check tag and diename */ if (dwarf_tag(sp_die) != DW_TAG_subprogram || !die_compare_name(sp_die, pp->function)) return DWARF_CB_OK; /* Check declared file */ if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die))) return DWARF_CB_OK; pf->fname = dwarf_decl_file(sp_die); if (pp->line) { /* Function relative line */ dwarf_decl_line(sp_die, &pf->lno); pf->lno += pp->line; param->retval = find_probe_point_by_line(pf); } else if (!dwarf_func_inline(sp_die)) { /* Real function */ if (pp->lazy_line) param->retval = find_probe_point_lazy(sp_die, pf); else { if (dwarf_entrypc(sp_die, &pf->addr) != 0) { pr_warning("Failed to get entry address of " "%s.\n", dwarf_diename(sp_die)); param->retval = -ENOENT; return DWARF_CB_ABORT; } pf->addr += pp->offset; /* TODO: Check the address in this function */ param->retval = call_probe_finder(sp_die, pf); } } else /* Inlined function: search instances */ param->retval = die_walk_instances(sp_die, probe_point_inline_cb, (void *)pf); return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ }
static int probe_point_lazy_walker(const char *fname, int lineno, Dwarf_Addr addr, void *data) { struct probe_finder *pf = data; int ret; if (!line_list__has_line(&pf->lcache, lineno) || strtailcmp(fname, pf->fname) != 0) return 0; pr_debug("Probe line found: line:%d addr:0x%llx\n", lineno, (unsigned long long)addr); pf->addr = addr; ret = call_probe_finder(NULL, pf); /* * Continue if no error, because the lazy pattern will match * to other lines */ return ret < 0 ? ret : 0; }
/** * cu_find_realpath - Find the realpath of the target file * @cu_die: A DIE(dwarf information entry) of CU(compilation Unit) * @fname: The tail filename of the target file * * Find the real(long) path of @fname in @cu_die. */ const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) { Dwarf_Files *files; size_t nfiles, i; const char *src = NULL; int ret; if (!fname) return NULL; ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); if (ret != 0) return NULL; for (i = 0; i < nfiles; i++) { src = dwarf_filesrc(files, i, NULL, NULL); if (strtailcmp(src, fname) == 0) break; } if (i == nfiles) return NULL; return src; }
/* Find probe point from its line number */ static int find_probe_point_by_line(struct probe_finder *pf) { Dwarf_Lines *lines; Dwarf_Line *line; size_t nlines, i; Dwarf_Addr addr; int lineno; int ret = 0; if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { pr_warning("No source lines found in this CU.\n"); return -ENOENT; } for (i = 0; i < nlines && ret == 0; i++) { line = dwarf_onesrcline(lines, i); if (dwarf_lineno(line, &lineno) != 0 || lineno != pf->lno) continue; /* TODO: Get fileno from line, but how? */ if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) continue; if (dwarf_lineaddr(line, &addr) != 0) { pr_warning("Failed to get the address of the line.\n"); return -ENOENT; } pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", (int)i, lineno, (uintmax_t)addr); pf->addr = addr; ret = convert_probe_point(NULL, pf); /* Continuing, because target line might be inlined. */ } return ret; }
static int find_probe_point_by_func(struct probe_finder *pf) { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); return _param.retval; } struct pubname_callback_param { char *function; char *file; Dwarf_Die *cu_die; Dwarf_Die *sp_die; int found; }; static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) { struct pubname_callback_param *param = data; if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) { if (dwarf_tag(param->sp_die) != DW_TAG_subprogram) return DWARF_CB_OK; if (die_compare_name(param->sp_die, param->function)) { if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die)) return DWARF_CB_OK; if (param->file && strtailcmp(param->file, dwarf_decl_file(param->sp_die))) return DWARF_CB_OK; param->found = 1; return DWARF_CB_ABORT; } } return DWARF_CB_OK; } static int debuginfo__find_probes(struct debuginfo *self, struct probe_finder *pf) { struct perf_probe_point *pp = &pf->pev->point; Dwarf_Off off, noff; size_t cuhl; Dwarf_Die *diep; int ret = 0; #if _ELFUTILS_PREREQ(0, 142) pf->cfi = dwarf_getcfi(self->dbg); #endif off = 0; line_list__init(&pf->lcache); if (pp->function) { struct pubname_callback_param pubname_param = { .function = pp->function, .file = pp->file, .cu_die = &pf->cu_die, .sp_die = &pf->sp_die, .found = 0, }; struct dwarf_callback_param probe_param = { .data = pf, }; dwarf_getpubnames(self->dbg, pubname_search_cb, &pubname_param, 0); if (pubname_param.found) { ret = probe_point_search_cb(&pf->sp_die, &probe_param); if (ret) goto found; } } while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die); if (!diep) continue; if (pp->file) pf->fname = cu_find_realpath(&pf->cu_die, pp->file); else pf->fname = NULL; if (!pp->file || pf->fname) { if (pp->function) ret = find_probe_point_by_func(pf); else if (pp->lazy_line) ret = find_probe_point_lazy(NULL, pf); else { pf->lno = pp->line; ret = find_probe_point_by_line(pf); } if (ret < 0) break; } off = noff; } found: line_list__free(&pf->lcache); return ret; } static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) { struct trace_event_finder *tf = container_of(pf, struct trace_event_finder, pf); struct probe_trace_event *tev; int ret, i; if (tf->ntevs == tf->max_tevs) { pr_warning("Too many( > %d) probe point found.\n", tf->max_tevs); return -ERANGE; } tev = &tf->tevs[tf->ntevs++]; ret = convert_to_trace_point(&pf->sp_die, pf->addr, pf->pev->point.retprobe, &tev->point); if (ret < 0) return ret; pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); tev->nargs = pf->pev->nargs; tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); if (tev->args == NULL) return -ENOMEM; for (i = 0; i < pf->pev->nargs; i++) { pf->pvar = &pf->pev->args[i]; pf->tvar = &tev->args[i]; ret = find_variable(sc_die, pf); if (ret != 0) return ret; } return 0; } int debuginfo__find_trace_events(struct debuginfo *self, struct perf_probe_event *pev, struct probe_trace_event **tevs, int max_tevs) { struct trace_event_finder tf = { .pf = {.pev = pev, .callback = add_probe_trace_event}, .max_tevs = max_tevs}; int ret; *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); if (*tevs == NULL) return -ENOMEM; tf.tevs = *tevs; tf.ntevs = 0; ret = debuginfo__find_probes(self, &tf.pf); if (ret < 0) { free(*tevs); *tevs = NULL; return ret; } return (ret < 0) ? ret : tf.ntevs; } #define MAX_VAR_LEN 64 static int collect_variables_cb(Dwarf_Die *die_mem, void *data) { struct available_var_finder *af = data; struct variable_list *vl; char buf[MAX_VAR_LEN]; int tag, ret; vl = &af->vls[af->nvls - 1]; tag = dwarf_tag(die_mem); if (tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) { ret = convert_variable_location(die_mem, af->pf.addr, af->pf.fb_ops, NULL); if (ret == 0) { ret = die_get_varname(die_mem, buf, MAX_VAR_LEN); pr_debug2("Add new var: %s\n", buf); if (ret > 0) strlist__add(vl->vars, buf); } } if (af->child && dwarf_haspc(die_mem, af->pf.addr)) return DIE_FIND_CB_CONTINUE; else return DIE_FIND_CB_SIBLING; } static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) { struct available_var_finder *af = container_of(pf, struct available_var_finder, pf); struct variable_list *vl; Dwarf_Die die_mem; int ret; if (af->nvls == af->max_vls) { pr_warning("Too many( > %d) probe point found.\n", af->max_vls); return -ERANGE; } vl = &af->vls[af->nvls++]; ret = convert_to_trace_point(&pf->sp_die, pf->addr, pf->pev->point.retprobe, &vl->point); if (ret < 0) return ret; pr_debug("Probe point found: %s+%lu\n", vl->point.symbol, vl->point.offset); vl->vars = strlist__new(true, NULL); if (vl->vars == NULL) return -ENOMEM; af->child = true; die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); if (!af->externs) goto out; af->child = false; die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem); out: if (strlist__empty(vl->vars)) { strlist__delete(vl->vars); vl->vars = NULL; } return ret; } int debuginfo__find_available_vars_at(struct debuginfo *self, struct perf_probe_event *pev, struct variable_list **vls, int max_vls, bool externs) { struct available_var_finder af = { .pf = {.pev = pev, .callback = add_available_vars}, .max_vls = max_vls, .externs = externs}; int ret; *vls = zalloc(sizeof(struct variable_list) * max_vls); if (*vls == NULL) return -ENOMEM; af.vls = *vls; af.nvls = 0; ret = debuginfo__find_probes(self, &af.pf); if (ret < 0) { while (af.nvls--) { if (af.vls[af.nvls].point.symbol) free(af.vls[af.nvls].point.symbol); if (af.vls[af.nvls].vars) strlist__delete(af.vls[af.nvls].vars); } free(af.vls); *vls = NULL; return ret; } return (ret < 0) ? ret : af.nvls; }
static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) { struct find_scope_param fsp = { .function = pf->pev->point.function, .file = pf->fname, .line = pf->lno, .diff = INT_MAX, .die_mem = die_mem, .found = false, }; cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp); return fsp.found ? die_mem : NULL; } static int probe_point_line_walker(const char *fname, int lineno, Dwarf_Addr addr, void *data) { struct probe_finder *pf = data; Dwarf_Die *sc_die, die_mem; int ret; if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) return 0; pf->addr = addr; sc_die = find_best_scope(pf, &die_mem); if (!sc_die) { pr_warning("Failed to find scope of probe point.\n"); return -ENOENT; } ret = call_probe_finder(sc_die, pf); return ret < 0 ? ret : 0; } static int find_probe_point_by_line(struct probe_finder *pf) { return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf); } static int find_lazy_match_lines(struct list_head *head, const char *fname, const char *pat) { FILE *fp; char *line = NULL; size_t line_len; ssize_t len; int count = 0, linenum = 1; fp = fopen(fname, "r"); if (!fp) { pr_warning("Failed to open %s: %s\n", fname, strerror(errno)); return -errno; } while ((len = getline(&line, &line_len, fp)) > 0) { if (line[len - 1] == '\n') line[len - 1] = '\0'; if (strlazymatch(line, pat)) { line_list__add_line(head, linenum); count++; } linenum++; } if (ferror(fp)) count = -errno; free(line); fclose(fp); if (count == 0) pr_debug("No matched lines found in %s.\n", fname); return count; }
static int find_probe_point_by_func(struct probe_finder *pf) { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); return _param.retval; } struct pubname_callback_param { char *function; char *file; Dwarf_Die *cu_die; Dwarf_Die *sp_die; int found; }; static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) { struct pubname_callback_param *param = data; if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) { if (dwarf_tag(param->sp_die) != DW_TAG_subprogram) return DWARF_CB_OK; if (die_compare_name(param->sp_die, param->function)) { if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die)) return DWARF_CB_OK; if (param->file && strtailcmp(param->file, dwarf_decl_file(param->sp_die))) return DWARF_CB_OK; param->found = 1; return DWARF_CB_ABORT; } } return DWARF_CB_OK; } /* Find probe points from debuginfo */ static int debuginfo__find_probes(struct debuginfo *dbg, struct probe_finder *pf) { struct perf_probe_point *pp = &pf->pev->point; Dwarf_Off off, noff; size_t cuhl; Dwarf_Die *diep; int ret = 0; #if _ELFUTILS_PREREQ(0, 142) Elf *elf; GElf_Ehdr ehdr; GElf_Shdr shdr; /* Get the call frame information from this dwarf */ elf = dwarf_getelf(dbg->dbg); if (elf == NULL) return -EINVAL; if (gelf_getehdr(elf, &ehdr) == NULL) return -EINVAL; if (elf_section_by_name(elf, &ehdr, &shdr, ".eh_frame", NULL) && shdr.sh_type == SHT_PROGBITS) { pf->cfi = dwarf_getcfi_elf(elf); } else { pf->cfi = dwarf_getcfi(dbg->dbg); } #endif off = 0; pf->lcache = intlist__new(NULL); if (!pf->lcache) return -ENOMEM; /* Fastpath: lookup by function name from .debug_pubnames section */ if (pp->function) { struct pubname_callback_param pubname_param = { .function = pp->function, .file = pp->file, .cu_die = &pf->cu_die, .sp_die = &pf->sp_die, .found = 0, }; struct dwarf_callback_param probe_param = { .data = pf, }; dwarf_getpubnames(dbg->dbg, pubname_search_cb, &pubname_param, 0); if (pubname_param.found) { ret = probe_point_search_cb(&pf->sp_die, &probe_param); if (ret) goto found; } } /* Loop on CUs (Compilation Unit) */ while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die); if (!diep) continue; /* Check if target file is included. */ if (pp->file) pf->fname = cu_find_realpath(&pf->cu_die, pp->file); else pf->fname = NULL; if (!pp->file || pf->fname) { if (pp->function) ret = find_probe_point_by_func(pf); else if (pp->lazy_line) ret = find_probe_point_lazy(&pf->cu_die, pf); else { pf->lno = pp->line; ret = find_probe_point_by_line(pf); } if (ret < 0) break; } off = noff; } found: intlist__delete(pf->lcache); pf->lcache = NULL; return ret; } struct local_vars_finder { struct probe_finder *pf; struct perf_probe_arg *args; int max_args; int nargs; int ret; }; /* Collect available variables in this scope */ static int copy_variables_cb(Dwarf_Die *die_mem, void *data) { struct local_vars_finder *vf = data; struct probe_finder *pf = vf->pf; int tag; tag = dwarf_tag(die_mem); if (tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) { if (convert_variable_location(die_mem, vf->pf->addr, vf->pf->fb_ops, &pf->sp_die, NULL) == 0) { vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); if (vf->args[vf->nargs].var == NULL) { vf->ret = -ENOMEM; return DIE_FIND_CB_END; } pr_debug(" %s", vf->args[vf->nargs].var); vf->nargs++; } } if (dwarf_haspc(die_mem, vf->pf->addr)) return DIE_FIND_CB_CONTINUE; else return DIE_FIND_CB_SIBLING; } static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf, struct perf_probe_arg *args) { Dwarf_Die die_mem; int i; int n = 0; struct local_vars_finder vf = {.pf = pf, .args = args, .max_args = MAX_PROBE_ARGS, .ret = 0}; for (i = 0; i < pf->pev->nargs; i++) { /* var never be NULL */ if (strcmp(pf->pev->args[i].var, "$vars") == 0) { pr_debug("Expanding $vars into:"); vf.nargs = n; /* Special local variables */ die_find_child(sc_die, copy_variables_cb, (void *)&vf, &die_mem); pr_debug(" (%d)\n", vf.nargs - n); if (vf.ret < 0) return vf.ret; n = vf.nargs; } else { /* Copy normal argument */ args[n] = pf->pev->args[i]; n++; } } return n; } /* Add a found probe point into trace event list */ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) { struct trace_event_finder *tf = container_of(pf, struct trace_event_finder, pf); struct probe_trace_event *tev; struct perf_probe_arg *args; int ret, i; /* Check number of tevs */ if (tf->ntevs == tf->max_tevs) { pr_warning("Too many( > %d) probe point found.\n", tf->max_tevs); return -ERANGE; } tev = &tf->tevs[tf->ntevs++]; /* Trace point should be converted from subprogram DIE */ ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr, pf->pev->point.retprobe, &tev->point); if (ret < 0) return ret; pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); /* Expand special probe argument if exist */ args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS); if (args == NULL) return -ENOMEM; ret = expand_probe_args(sc_die, pf, args); if (ret < 0) goto end; tev->nargs = ret; tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); if (tev->args == NULL) { ret = -ENOMEM; goto end; } /* Find each argument */ for (i = 0; i < tev->nargs; i++) { pf->pvar = &args[i]; pf->tvar = &tev->args[i]; /* Variable should be found from scope DIE */ ret = find_variable(sc_die, pf); if (ret != 0) break; } end: free(args); return ret; } /* Find probe_trace_events specified by perf_probe_event from debuginfo */ int debuginfo__find_trace_events(struct debuginfo *dbg, struct perf_probe_event *pev, struct probe_trace_event **tevs, int max_tevs) { struct trace_event_finder tf = { .pf = {.pev = pev, .callback = add_probe_trace_event}, .mod = dbg->mod, .max_tevs = max_tevs}; int ret; /* Allocate result tevs array */ *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); if (*tevs == NULL) return -ENOMEM; tf.tevs = *tevs; tf.ntevs = 0; ret = debuginfo__find_probes(dbg, &tf.pf); if (ret < 0) { zfree(tevs); return ret; } return (ret < 0) ? ret : tf.ntevs; } #define MAX_VAR_LEN 64 /* Collect available variables in this scope */ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) { struct available_var_finder *af = data; struct variable_list *vl; char buf[MAX_VAR_LEN]; int tag, ret; vl = &af->vls[af->nvls - 1]; tag = dwarf_tag(die_mem); if (tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) { ret = convert_variable_location(die_mem, af->pf.addr, af->pf.fb_ops, &af->pf.sp_die, NULL); if (ret == 0) { ret = die_get_varname(die_mem, buf, MAX_VAR_LEN); pr_debug2("Add new var: %s\n", buf); if (ret > 0) strlist__add(vl->vars, buf); } } if (af->child && dwarf_haspc(die_mem, af->pf.addr)) return DIE_FIND_CB_CONTINUE; else return DIE_FIND_CB_SIBLING; } /* Add a found vars into available variables list */ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) { struct available_var_finder *af = container_of(pf, struct available_var_finder, pf); struct variable_list *vl; Dwarf_Die die_mem; int ret; /* Check number of tevs */ if (af->nvls == af->max_vls) { pr_warning("Too many( > %d) probe point found.\n", af->max_vls); return -ERANGE; } vl = &af->vls[af->nvls++]; /* Trace point should be converted from subprogram DIE */ ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr, pf->pev->point.retprobe, &vl->point); if (ret < 0) return ret; pr_debug("Probe point found: %s+%lu\n", vl->point.symbol, vl->point.offset); /* Find local variables */ vl->vars = strlist__new(true, NULL); if (vl->vars == NULL) return -ENOMEM; af->child = true; die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); /* Find external variables */ if (!af->externs) goto out; /* Don't need to search child DIE for externs. */ af->child = false; die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem); out: if (strlist__empty(vl->vars)) { strlist__delete(vl->vars); vl->vars = NULL; } return ret; } /* * Find available variables at given probe point * Return the number of found probe points. Return 0 if there is no * matched probe point. Return <0 if an error occurs. */ int debuginfo__find_available_vars_at(struct debuginfo *dbg, struct perf_probe_event *pev, struct variable_list **vls, int max_vls, bool externs) { struct available_var_finder af = { .pf = {.pev = pev, .callback = add_available_vars}, .mod = dbg->mod, .max_vls = max_vls, .externs = externs}; int ret; /* Allocate result vls array */ *vls = zalloc(sizeof(struct variable_list) * max_vls); if (*vls == NULL) return -ENOMEM; af.vls = *vls; af.nvls = 0; ret = debuginfo__find_probes(dbg, &af.pf); if (ret < 0) { /* Free vlist for error */ while (af.nvls--) { zfree(&af.vls[af.nvls].point.symbol); strlist__delete(af.vls[af.nvls].vars); } zfree(vls); return ret; } return (ret < 0) ? ret : af.nvls; } /* Reverse search */ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, struct perf_probe_point *ppt) { Dwarf_Die cudie, spdie, indie; Dwarf_Addr _addr = 0, baseaddr = 0; const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp; int baseline = 0, lineno = 0, ret = 0; /* Find cu die */ if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) { pr_warning("Failed to find debug information for address %lx\n", addr); ret = -EINVAL; goto end; } /* Find a corresponding line (filename and lineno) */ cu_find_lineinfo(&cudie, addr, &fname, &lineno); /* Don't care whether it failed or not */ /* Find a corresponding function (name, baseline and baseaddr) */ if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { /* Get function entry information */ func = basefunc = dwarf_diename(&spdie); if (!func || dwarf_entrypc(&spdie, &baseaddr) != 0 || dwarf_decl_line(&spdie, &baseline) != 0) { lineno = 0; goto post; } fname = dwarf_decl_file(&spdie); if (addr == (unsigned long)baseaddr) { /* Function entry - Relative line number is 0 */ lineno = baseline; goto post; } /* Track down the inline functions step by step */ while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) { /* There is an inline function */ if (dwarf_entrypc(&indie, &_addr) == 0 && _addr == addr) { /* * addr is at an inline function entry. * In this case, lineno should be the call-site * line number. (overwrite lineinfo) */ lineno = die_get_call_lineno(&indie); fname = die_get_call_file(&indie); break; } else { /* * addr is in an inline function body. * Since lineno points one of the lines * of the inline function, baseline should * be the entry line of the inline function. */ tmp = dwarf_diename(&indie); if (!tmp || dwarf_decl_line(&indie, &baseline) != 0) break; func = tmp; spdie = indie; } } /* Verify the lineno and baseline are in a same file */ tmp = dwarf_decl_file(&spdie); if (!tmp || strcmp(tmp, fname) != 0) lineno = 0; } post: /* Make a relative line number or an offset */ if (lineno) ppt->line = lineno - baseline; else if (basefunc) { ppt->offset = addr - (unsigned long)baseaddr; func = basefunc; } /* Duplicate strings */ if (func) { ppt->function = strdup(func); if (ppt->function == NULL) { ret = -ENOMEM; goto end; } } if (fname) { ppt->file = strdup(fname); if (ppt->file == NULL) { zfree(&ppt->function); ret = -ENOMEM; goto end; } } end: if (ret == 0 && (fname || func)) ret = 1; /* Found a point */ return ret; } /* Add a line and store the src path */ static int line_range_add_line(const char *src, unsigned int lineno, struct line_range *lr) { /* Copy source path */ if (!lr->path) { lr->path = strdup(src); if (lr->path == NULL) return -ENOMEM; } return intlist__add(lr->line_list, lineno); } static int line_range_walk_cb(const char *fname, int lineno, Dwarf_Addr addr __maybe_unused, void *data) { struct line_finder *lf = data; int err; if ((strtailcmp(fname, lf->fname) != 0) || (lf->lno_s > lineno || lf->lno_e < lineno)) return 0; err = line_range_add_line(fname, lineno, lf->lr); if (err < 0 && err != -EEXIST) return err; return 0; } /* Find line range from its line number */ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) { int ret; ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf); /* Update status */ if (ret >= 0) if (!intlist__empty(lf->lr->line_list)) ret = lf->found = 1; else ret = 0; /* Lines are not found */ else { zfree(&lf->lr->path); } return ret; }
/* Search function from function name */ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) { struct dwarf_callback_param *param = data; struct probe_finder *pf = param->data; struct perf_probe_point *pp = &pf->pev->point; /* Check tag and diename */ if (dwarf_tag(sp_die) != DW_TAG_subprogram || die_compare_name(sp_die, pp->function) != 0) return DWARF_CB_OK; pf->fname = dwarf_decl_file(sp_die); if (pp->line) { /* Function relative line */ dwarf_decl_line(sp_die, &pf->lno); pf->lno += pp->line; param->retval = find_probe_point_by_line(pf); } else if (!dwarf_func_inline(sp_die)) { /* Real function */ if (pp->lazy_line) param->retval = find_probe_point_lazy(sp_die, pf); else { if (dwarf_entrypc(sp_die, &pf->addr) != 0) { pr_warning("Failed to get entry pc of %s.\n", dwarf_diename(sp_die)); param->retval = -ENOENT; return DWARF_CB_ABORT; } pf->addr += pp->offset; /* TODO: Check the address in this function */ param->retval = convert_probe_point(sp_die, pf); } } else { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; /* Inlined function: search instances */ dwarf_func_inline_instances(sp_die, probe_point_inline_cb, &_param); param->retval = _param.retval; } return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ } static int find_probe_point_by_func(struct probe_finder *pf) { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); return _param.retval; } /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, struct kprobe_trace_event **tevs, int max_tevs) { struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; struct perf_probe_point *pp = &pev->point; Dwarf_Off off, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; int ret = 0; pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); if (pf.tevs == NULL) return -ENOMEM; *tevs = pf.tevs; pf.ntevs = 0; dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) { pr_warning("No dwarf info found in the vmlinux - " "please rebuild with CONFIG_DEBUG_INFO=y.\n"); free(pf.tevs); *tevs = NULL; return -EBADF; } #if _ELFUTILS_PREREQ(0, 142) /* Get the call frame information from this dwarf */ pf.cfi = dwarf_getcfi(dbg); #endif off = 0; line_list__init(&pf.lcache); /* Loop on CUs (Compilation Unit) */ while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && ret >= 0) { /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); if (!diep) continue; /* Check if target file is included. */ if (pp->file) pf.fname = cu_find_realpath(&pf.cu_die, pp->file); else pf.fname = NULL; if (!pp->file || pf.fname) { if (pp->function) ret = find_probe_point_by_func(&pf); else if (pp->lazy_line) ret = find_probe_point_lazy(NULL, &pf); else { pf.lno = pp->line; ret = find_probe_point_by_line(&pf); } } off = noff; } line_list__free(&pf.lcache); dwarf_end(dbg); return (ret < 0) ? ret : pf.ntevs; } /* Reverse search */ int find_perf_probe_point(int fd, unsigned long addr, struct perf_probe_point *ppt) { Dwarf_Die cudie, spdie, indie; Dwarf *dbg; Dwarf_Line *line; Dwarf_Addr laddr, eaddr; const char *tmp; int lineno, ret = 0; bool found = false; dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) return -EBADF; /* Find cu die */ if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { ret = -EINVAL; goto end; } /* Find a corresponding line */ line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); if (line) { if (dwarf_lineaddr(line, &laddr) == 0 && (Dwarf_Addr)addr == laddr && dwarf_lineno(line, &lineno) == 0) { tmp = dwarf_linesrc(line, NULL, NULL); if (tmp) { ppt->line = lineno; ppt->file = strdup(tmp); if (ppt->file == NULL) { ret = -ENOMEM; goto end; } found = true; } } } /* Find a corresponding function */ if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { tmp = dwarf_diename(&spdie); if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0) goto end; if (ppt->line) { if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) { /* addr in an inline function */ tmp = dwarf_diename(&indie); if (!tmp) goto end; ret = dwarf_decl_line(&indie, &lineno); } else { if (eaddr == addr) { /* Function entry */ lineno = ppt->line; ret = 0; } else ret = dwarf_decl_line(&spdie, &lineno); } if (ret == 0) { /* Make a relative line number */ ppt->line -= lineno; goto found; } } /* We don't have a line number, let's use offset */ ppt->offset = addr - (unsigned long)eaddr; found: ppt->function = strdup(tmp); if (ppt->function == NULL) { ret = -ENOMEM; goto end; } found = true; } end: dwarf_end(dbg); if (ret >= 0) ret = found ? 1 : 0; return ret; } /* Add a line and store the src path */ static int line_range_add_line(const char *src, unsigned int lineno, struct line_range *lr) { /* Copy real path */ if (!lr->path) { lr->path = strdup(src); if (lr->path == NULL) return -ENOMEM; } return line_list__add_line(&lr->line_list, lineno); } /* Search function declaration lines */ static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) { struct dwarf_callback_param *param = data; struct line_finder *lf = param->data; const char *src; int lineno; src = dwarf_decl_file(sp_die); if (src && strtailcmp(src, lf->fname) != 0) return DWARF_CB_OK; if (dwarf_decl_line(sp_die, &lineno) != 0 || (lf->lno_s > lineno || lf->lno_e < lineno)) return DWARF_CB_OK; param->retval = line_range_add_line(src, lineno, lf->lr); if (param->retval < 0) return DWARF_CB_ABORT; return DWARF_CB_OK; } static int find_line_range_func_decl_lines(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); return param.retval; } /* Find line range from its line number */ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) { Dwarf_Lines *lines; Dwarf_Line *line; size_t nlines, i; Dwarf_Addr addr; int lineno, ret = 0; const char *src; Dwarf_Die die_mem; line_list__init(&lf->lr->line_list); if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { pr_warning("No source lines found in this CU.\n"); return -ENOENT; } /* Search probable lines on lines list */ for (i = 0; i < nlines; i++) { line = dwarf_onesrcline(lines, i); if (dwarf_lineno(line, &lineno) != 0 || (lf->lno_s > lineno || lf->lno_e < lineno)) continue; if (sp_die) { /* Address filtering 1: does sp_die include addr? */ if (dwarf_lineaddr(line, &addr) != 0 || !dwarf_haspc(sp_die, addr)) continue; /* Address filtering 2: No child include addr? */ if (die_find_inlinefunc(sp_die, addr, &die_mem)) continue; } /* TODO: Get fileno from line, but how? */ src = dwarf_linesrc(line, NULL, NULL); if (strtailcmp(src, lf->fname) != 0) continue; ret = line_range_add_line(src, lineno, lf->lr); if (ret < 0) return ret; } /* * Dwarf lines doesn't include function declarations. We have to * check functions list or given function. */ if (sp_die) { src = dwarf_decl_file(sp_die); if (src && dwarf_decl_line(sp_die, &lineno) == 0 && (lf->lno_s <= lineno && lf->lno_e >= lineno)) ret = line_range_add_line(src, lineno, lf->lr); } else ret = find_line_range_func_decl_lines(lf); /* Update status */ if (ret >= 0) if (!list_empty(&lf->lr->line_list)) ret = lf->found = 1; else ret = 0; /* Lines are not found */ else { free(lf->lr->path); lf->lr->path = NULL; } return ret; } static int line_range_inline_cb(Dwarf_Die *in_die, void *data) { struct dwarf_callback_param *param = data; param->retval = find_line_range_by_line(in_die, param->data); return DWARF_CB_ABORT; /* No need to find other instances */ } /* Search function from function name */ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) { struct dwarf_callback_param *param = data; struct line_finder *lf = param->data; struct line_range *lr = lf->lr; if (dwarf_tag(sp_die) == DW_TAG_subprogram && die_compare_name(sp_die, lr->function) == 0) { lf->fname = dwarf_decl_file(sp_die); dwarf_decl_line(sp_die, &lr->offset); pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); lf->lno_s = lr->offset + lr->start; if (lf->lno_s < 0) /* Overflow */ lf->lno_s = INT_MAX; lf->lno_e = lr->offset + lr->end; if (lf->lno_e < 0) /* Overflow */ lf->lno_e = INT_MAX; pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); lr->start = lf->lno_s; lr->end = lf->lno_e; if (dwarf_func_inline(sp_die)) { struct dwarf_callback_param _param; _param.data = (void *)lf; _param.retval = 0; dwarf_func_inline_instances(sp_die, line_range_inline_cb, &_param); param->retval = _param.retval; } else param->retval = find_line_range_by_line(sp_die, lf); return DWARF_CB_ABORT; } return DWARF_CB_OK; } static int find_line_range_by_func(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); return param.retval; } int find_line_range(int fd, struct line_range *lr) { struct line_finder lf = {.lr = lr, .found = 0}; int ret = 0; Dwarf_Off off = 0, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) { pr_warning("No dwarf info found in the vmlinux - " "please rebuild with CONFIG_DEBUG_INFO=y.\n"); return -EBADF; } /* Loop on CUs (Compilation Unit) */ while (!lf.found && ret >= 0) { if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) break; /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); if (!diep) continue; /* Check if target file is included. */ if (lr->file) lf.fname = cu_find_realpath(&lf.cu_die, lr->file); else lf.fname = 0; if (!lr->file || lf.fname) { if (lr->function) ret = find_line_range_by_func(&lf); else { lf.lno_s = lr->start; lf.lno_e = lr->end; ret = find_line_range_by_line(NULL, &lf); } } off = noff; } pr_debug("path: %lx\n", (unsigned long)lr->path); dwarf_end(dbg); return (ret < 0) ? ret : lf.found; }
/* Find probe points from lazy pattern */ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) { Dwarf_Lines *lines; Dwarf_Line *line; size_t nlines, i; Dwarf_Addr addr; Dwarf_Die die_mem; int lineno; int ret = 0; if (list_empty(&pf->lcache)) { /* Matching lazy line pattern */ ret = find_lazy_match_lines(&pf->lcache, pf->fname, pf->pev->point.lazy_line); if (ret == 0) { pr_debug("No matched lines found in %s.\n", pf->fname); return 0; } else if (ret < 0) return ret; } if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { pr_warning("No source lines found in this CU.\n"); return -ENOENT; } for (i = 0; i < nlines && ret >= 0; i++) { line = dwarf_onesrcline(lines, i); if (dwarf_lineno(line, &lineno) != 0 || !line_list__has_line(&pf->lcache, lineno)) continue; /* TODO: Get fileno from line, but how? */ if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) continue; if (dwarf_lineaddr(line, &addr) != 0) { pr_debug("Failed to get the address of line %d.\n", lineno); continue; } if (sp_die) { /* Address filtering 1: does sp_die include addr? */ if (!dwarf_haspc(sp_die, addr)) continue; /* Address filtering 2: No child include addr? */ if (die_find_inlinefunc(sp_die, addr, &die_mem)) continue; } pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", (int)i, lineno, (unsigned long long)addr); pf->addr = addr; ret = convert_probe_point(sp_die, pf); /* Continuing, because target line might be inlined. */ } /* TODO: deallocate lines, but how? */ return ret; }
int fsck(const char *argv0) { #ifdef HAVE_MINGW int uid = 0; #else uid_t uid = getuid(); #endif // Check that tinc.conf is readable. if(access(tinc_conf, R_OK)) { fprintf(stderr, "ERROR: cannot read %s: %s\n", tinc_conf, strerror(errno)); if(errno == ENOENT) { fprintf(stderr, "No tinc configuration found. Create a new one with:\n\n"); print_tinc_cmd(argv0, "init"); } else if(errno == EACCES) { if(uid != 0) fprintf(stderr, "You are currently not running tinc as root. Use sudo?\n"); else fprintf(stderr, "Check the permissions of each component of the path %s.\n", tinc_conf); } return 1; } char *name = get_my_name(true); if(!name) { fprintf(stderr, "ERROR: tinc cannot run without a valid Name.\n"); return 1; } // Check for private keys. // TODO: use RSAPrivateKeyFile and Ed25519PrivateKeyFile variables if present. struct stat st; char fname[PATH_MAX]; char dname[PATH_MAX]; ecdsa_t *ecdsa_priv = NULL; snprintf(fname, sizeof fname, "%s/ed25519_key.priv", confbase); if(stat(fname, &st)) { if(errno != ENOENT) { // Something is seriously wrong here. If we can access the directory with tinc.conf in it, we should certainly be able to stat() an existing file. fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno)); fprintf(stderr, "Please correct this error.\n"); return 1; } } else { FILE *f = fopen(fname, "r"); if(!f) { fprintf(stderr, "ERROR: could not open %s: %s\n", fname, strerror(errno)); return 1; } ecdsa_priv = ecdsa_read_pem_private_key(f); fclose(f); if(!ecdsa_priv) { fprintf(stderr, "ERROR: No key or unusable key found in %s.\n", fname); fprintf(stderr, "You can generate a new Ed25519 key with:\n\n"); print_tinc_cmd(argv0, "generate-ed25519-keys"); return 1; } if(st.st_mode & 077) { fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname); if(st.st_uid != uid) { fprintf(stderr, "You are not running %s as the same uid as %s.\n", argv0, fname); } else if(ask_fix()) { if(chmod(fname, st.st_mode & ~077)) fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno)); else fprintf(stderr, "Fixed permissions of %s.\n", fname); } } } if(!ecdsa_priv) { fprintf(stderr, "ERROR: No Ed25519 private key found.\n"); fprintf(stderr, "You can generate new keys with:\n\n"); print_tinc_cmd(argv0, "generate-keys"); return 1; } // Check for public keys. // TODO: use RSAPublicKeyFile and Ed25519PublicKeyFile variables if present. snprintf(fname, sizeof fname, "%s/hosts/%s", confbase, name); if(access(fname, R_OK)) fprintf(stderr, "WARNING: cannot read %s\n", fname); FILE *f; // TODO: this should read the Ed25519PublicKey config variable instead. ecdsa_t *ecdsa_pub = NULL; f = fopen(fname, "r"); if(f) ecdsa_pub = ecdsa_read_pem_public_key(f); fclose(f); if(ecdsa_priv) { if(!ecdsa_pub) { fprintf(stderr, "WARNING: No (usable) public Ed25519 key found.\n"); if(ask_fix()) { FILE *f = fopen(fname, "a"); if(f) { if(ecdsa_write_pem_public_key(ecdsa_priv, f)) fprintf(stderr, "Wrote Ed25519 public key to %s.\n", fname); else fprintf(stderr, "ERROR: could not write Ed25519 public key to %s.\n", fname); fclose(f); } else { fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno)); } } } else { // TODO: suggest remedies char *key1 = ecdsa_get_base64_public_key(ecdsa_pub); if(!key1) { fprintf(stderr, "ERROR: public Ed25519 key does not work.\n"); return 1; } char *key2 = ecdsa_get_base64_public_key(ecdsa_priv); if(!key2) { free(key1); fprintf(stderr, "ERROR: private Ed25519 key does not work.\n"); return 1; } int result = strcmp(key1, key2); free(key1); free(key2); if(result) { fprintf(stderr, "ERROR: public and private Ed25519 keys do not match.\n"); return 1; } } } else { if(ecdsa_pub) fprintf(stderr, "WARNING: A public Ed25519 key was found but no private key is known.\n"); } // Check whether scripts are executable struct dirent *ent; DIR *dir = opendir(confbase); if(!dir) { fprintf(stderr, "ERROR: cannot read directory %s: %s\n", confbase, strerror(errno)); return 1; } while((ent = readdir(dir))) { if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) continue; strncpy(fname, ent->d_name, sizeof fname); char *dash = strrchr(fname, '-'); if(!dash) continue; *dash = 0; if(strcmp(fname, "tinc") && strcmp(fname, "host") && strcmp(fname, "subnet")) { static bool explained = false; fprintf(stderr, "WARNING: Unknown script %s" SLASH "%s found.\n", confbase, ent->d_name); if(!explained) { fprintf(stderr, "The only scripts in %s executed by tinc are:\n", confbase); fprintf(stderr, "tinc-up, tinc-down, host-up, host-down, subnet-up and subnet-down.\n"); explained = true; } continue; } snprintf(fname, sizeof fname, "%s" SLASH "%s", confbase, ent->d_name); if(access(fname, R_OK | X_OK)) { if(errno != EACCES) { fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno)); continue; } fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno)); if(ask_fix()) { if(chmod(fname, 0755)) fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno)); } } } closedir(dir); snprintf(dname, sizeof dname, "%s" SLASH "hosts", confbase); dir = opendir(dname); if(!dir) { fprintf(stderr, "ERROR: cannot read directory %s: %s\n", dname, strerror(errno)); return 1; } while((ent = readdir(dir))) { if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) continue; strncpy(fname, ent->d_name, sizeof fname); char *dash = strrchr(fname, '-'); if(!dash) continue; *dash = 0; snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name); if(access(fname, R_OK | X_OK)) { if(errno != EACCES) { fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno)); continue; } fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno)); if(ask_fix()) { if(chmod(fname, 0755)) fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno)); } } } closedir(dir); // Check for obsolete / unsafe / unknown configuration variables. check_conffile(tinc_conf, true); dir = opendir(dname); if(dir) { while((ent = readdir(dir))) { if(!check_id(ent->d_name)) continue; snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name); check_conffile(fname, false); } closedir(dir); } return 0; }