in cli/bpfcov.c [203:392]
static error_t root_parse(int key, char *arg, struct argp_state *state)
{
struct root_args *args = state->input;
char str[2];
log_debu(args, "parsing <root> %s = '%s'\n", argp_key(key, str), arg ? arg : "(null)");
switch (key)
{
// Initialization
case ARGP_KEY_INIT:
args->bpffs = "/sys/fs/bpf";
// args->verbosity = 0; // It needs to be set before the parsing starts
args->command = NULL;
args->program = calloc(PATH_MAX, sizeof(char *));
args->output = NULL;
args->unpin = false;
break;
case ROOT_BPFFS_OPT_KEY:
if (strlen(arg) > 0)
{
strip_trailing_char(arg, '/');
args->bpffs = arg;
break;
}
argp_error(state, "option '--%s' requires a %s", ROOT_BPFFS_OPT_LONG, ROOT_BPFFS_OPT_ARG);
break;
case ROOT_VERBOSITY_OPT_KEY:
if (arg)
{
errno = 0;
char *end;
long num = strtol(arg, &end, 10);
if (end == arg)
{
argp_error(state, "option '--%s' requires a numeric %s", ROOT_VERBOSITY_OPT_LONG, ROOT_VERBOSITY_OPT_ARG);
}
if (num < 0 || num > 3)
{
argp_error(state, "option '--%s' requires a %s value in [0,3]", ROOT_VERBOSITY_OPT_LONG, ROOT_VERBOSITY_OPT_ARG);
}
args->verbosity = (int)num;
}
else
{
args->verbosity++;
}
break;
case ARGP_KEY_ARG:
assert(arg);
/**/ if (strncmp(arg, "run", 3) == 0)
{
args->command = &run;
run_cmd(state);
}
else if (strncmp(arg, "gen", 3) == 0)
{
args->command = &gen;
gen_cmd(state);
}
else if (strncmp(arg, "out", 3) == 0)
{
args->command = &out;
out_cmd(state);
}
else
{
args->program[state->arg_num] = arg;
}
break;
// Args validation
case ARGP_KEY_END:
if (state->arg_num == 0)
{
argp_state_help(state, state->err_stream, ARGP_HELP_STD_HELP);
}
if (args->command != &out && args->program[0] == NULL)
{
// This should never happen
argp_error(state, "unexpected missing <program>");
}
break;
// Final validations, checks, and settings
case ARGP_KEY_FINI:
bool is_run = args->command == &run;
// When the subcommand is <out>
// - do not validate BPF FS
// - do not generate pinning paths
// - do not clean up (<run>) or check (<gen>) pinned maps
if (args->command == &out)
{
break;
}
// Check the BPF filesystem
if (!is_bpffs(args->bpffs))
{
argp_error(state, "the BPF filesystem is not mounted at %s", args->bpffs);
}
// Create the coverage directory in the BPF filesystem
char cov_root[PATH_MAX];
int cov_root_len = snprintf(cov_root, PATH_MAX, "%s/%s", args->bpffs, "cov");
if (cov_root_len >= PATH_MAX)
{
argp_error(state, "coverage root path too long");
}
if (is_run && mkdir(cov_root, 0700) && errno != EEXIST)
{
argp_error(state, "could not create '%s'", cov_root);
}
args->cov_root = cov_root;
// Obtain the program name and create a directory in the BPF filesystem for it
char *prog_name = basename(args->program[0]);
char prog_root[PATH_MAX];
int prog_root_len = snprintf(prog_root, PATH_MAX, "%s/%s", cov_root, prog_name);
if (prog_root_len >= PATH_MAX)
{
argp_error(state, "program root path too long");
}
char *prog_root_sane = strdup(prog_root);
replace_with(prog_root_sane, '.', '_'); // Sanitize because BPF FS doesn't accept dots
if (is_run && mkdir(prog_root_sane, 0700) && errno != EEXIST)
{
argp_error(state, "could not create '%s'", prog_root_sane);
}
args->prog_root = prog_root_sane;
log_info(args, "root directory for map pins at '%s'\n", prog_root_sane);
// Create pinning path for the counters map
char pin_profc[PATH_MAX];
int pin_profc_len = snprintf(pin_profc, PATH_MAX, "%s/%s", prog_root_sane, "profc");
if (pin_profc_len >= PATH_MAX)
{
argp_error(state, "counters pinning path too long");
}
args->pin[0] = pin_profc;
// Create pinning path for the data map
char pin_profd[PATH_MAX];
int pin_profd_len = snprintf(pin_profd, PATH_MAX, "%s/%s", prog_root_sane, "profd");
if (pin_profd_len >= PATH_MAX)
{
argp_error(state, "data pinning path too long");
}
args->pin[1] = pin_profd;
// Create pinning path for the names map
char pin_profn[PATH_MAX];
int pin_profn_len = snprintf(pin_profn, PATH_MAX, "%s/%s", prog_root_sane, "profn");
if (pin_profn_len >= PATH_MAX)
{
argp_error(state, "names pinning path too long");
}
args->pin[2] = pin_profn;
// Create pinning path for the coverage mapping header
char pin_covmap[PATH_MAX];
int pin_covmap_len = snprintf(pin_covmap, PATH_MAX, "%s/%s", prog_root_sane, "covmap");
if (pin_covmap_len >= PATH_MAX)
{
argp_error(state, "coverage mapping header path too long");
}
args->pin[3] = pin_covmap;
// Check whether the map pinning paths already exist:
// - unpin them in case they do exist and the current subcommand is `run`
// - error out in case the do not exist and the current subcommand is `gen`
handle_map_pins(args, state, is_run);
break;
default:
log_debu(args, "parsing <root> UNKNOWN = '%s'\n", arg ? arg : "(null)");
return ARGP_ERR_UNKNOWN;
}
return 0;
}