mirror of
https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git
synced 2026-10-24 00:48:53 +03:00
struct line_resolver should be freed with free_line_resolver() while
gpioinfo uses a regular free() resulting in a leak of multiple objects.
This issue is not really harmful as gpioinfo exits right after that but
fix it for consistency.
Fixes: 8ffb648928 ("tools: line name focussed rework")
Reported-by: 4fqr <4fqr@proton.me>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260407-treewide-fixes-v1-13-66c9744a56a3@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
273 lines
6.3 KiB
C
273 lines
6.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com>
|
|
// SPDX-FileCopyrightText: 2022 Kent Gibson <warthog618@gmail.com>
|
|
|
|
#include <getopt.h>
|
|
#include <gpiod.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "tools-common.h"
|
|
|
|
struct config {
|
|
bool by_name;
|
|
bool strict;
|
|
bool unquoted_strings;
|
|
const char *chip_id;
|
|
};
|
|
|
|
static void print_help(void)
|
|
{
|
|
printf("Usage: %s [OPTIONS] [line]...\n", get_prog_name());
|
|
printf("\n");
|
|
printf("Print information about GPIO lines.\n");
|
|
printf("\n");
|
|
printf("Lines are specified by name, or optionally by offset if the chip option\n");
|
|
printf("is provided.\n");
|
|
printf("\n");
|
|
printf("If no lines are specified then all lines are displayed.\n");
|
|
printf("\n");
|
|
printf("Options:\n");
|
|
printf(" --by-name\t\ttreat lines as names even if they would parse as an offset\n");
|
|
printf(" -c, --chip <chip>\trestrict scope to a particular chip\n");
|
|
printf(" -h, --help\t\tdisplay this help and exit\n");
|
|
printf(" -s, --strict\t\tcheck all lines - don't assume line names are unique\n");
|
|
printf(" --unquoted\tdon't quote line or consumer names\n");
|
|
printf(" -v, --version\t\toutput version information and exit\n");
|
|
print_chip_help();
|
|
}
|
|
|
|
static int parse_config(int argc, char **argv, struct config *cfg)
|
|
{
|
|
static const struct option longopts[] = {
|
|
{ "by-name", no_argument, NULL, 'B' },
|
|
{ "chip", required_argument, NULL, 'c' },
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "strict", no_argument, NULL, 's' },
|
|
{ "unquoted", no_argument, NULL, 'Q' },
|
|
{ "version", no_argument, NULL, 'v' },
|
|
{ GETOPT_NULL_LONGOPT },
|
|
};
|
|
|
|
|
|
static const char *const shortopts = "+c:hsv";
|
|
|
|
int opti, optc;
|
|
|
|
memset(cfg, 0, sizeof(*cfg));
|
|
|
|
for (;;) {
|
|
optc = getopt_long(argc, argv, shortopts, longopts, &opti);
|
|
if (optc < 0)
|
|
break;
|
|
|
|
switch (optc) {
|
|
case 'B':
|
|
cfg->by_name = true;
|
|
break;
|
|
case 'c':
|
|
cfg->chip_id = optarg;
|
|
break;
|
|
case 's':
|
|
cfg->strict = true;
|
|
break;
|
|
case 'h':
|
|
print_help();
|
|
exit(EXIT_SUCCESS);
|
|
case 'Q':
|
|
cfg->unquoted_strings = true;
|
|
break;
|
|
case 'v':
|
|
print_version();
|
|
exit(EXIT_SUCCESS);
|
|
case '?':
|
|
die("try %s --help", get_prog_name());
|
|
case 0:
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
return optind;
|
|
}
|
|
|
|
/*
|
|
* Minimal version similar to tools-common that indicates if a line should be
|
|
* printed rather than storing details into the resolver.
|
|
* Does not die on non-unique lines.
|
|
*/
|
|
static bool resolve_line(struct line_resolver *resolver,
|
|
struct gpiod_line_info *info, int chip_num)
|
|
{
|
|
struct resolved_line *line;
|
|
bool resolved = false;
|
|
unsigned int offset;
|
|
const char *name;
|
|
int i;
|
|
|
|
offset = gpiod_line_info_get_offset(info);
|
|
|
|
for (i = 0; i < resolver->num_lines; i++) {
|
|
line = &resolver->lines[i];
|
|
|
|
/* already resolved by offset? */
|
|
if (line->resolved && (line->offset == offset) &&
|
|
(line->chip_num == chip_num)) {
|
|
resolved = true;
|
|
}
|
|
|
|
if (line->resolved && !resolver->strict)
|
|
continue;
|
|
|
|
/* else resolve by name */
|
|
name = gpiod_line_info_get_name(info);
|
|
if (name && (strcmp(line->id, name) == 0)) {
|
|
line->resolved = true;
|
|
line->offset = offset;
|
|
line->chip_num = chip_num;
|
|
resolved = true;
|
|
}
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
static void print_line_info(struct gpiod_line_info *info, bool unquoted_strings)
|
|
{
|
|
bool unquoted_name = unquoted_strings;
|
|
char quoted_name[17];
|
|
const char *name;
|
|
int len;
|
|
|
|
name = gpiod_line_info_get_name(info);
|
|
if (!name) {
|
|
name = "unnamed";
|
|
unquoted_name = true;
|
|
}
|
|
|
|
if (unquoted_name) {
|
|
printf("%-16s\t", name);
|
|
} else {
|
|
len = strlen(name);
|
|
if (len <= 14) {
|
|
quoted_name[0] = '"';
|
|
memcpy("ed_name[1], name, len);
|
|
quoted_name[len + 1] = '"';
|
|
quoted_name[len + 2] = '\0';
|
|
printf("%-16s\t", quoted_name);
|
|
} else {
|
|
printf("\"%s\"\t", name);
|
|
}
|
|
}
|
|
|
|
print_line_attributes(info, unquoted_strings);
|
|
}
|
|
|
|
/*
|
|
* based on resolve_lines, but prints lines immediately rather than collecting
|
|
* details in the resolver.
|
|
*/
|
|
static void list_lines(struct line_resolver *resolver, struct gpiod_chip *chip,
|
|
int chip_num, struct config *cfg)
|
|
{
|
|
struct gpiod_chip_info *chip_info;
|
|
struct gpiod_line_info *info;
|
|
int offset, num_lines;
|
|
|
|
chip_info = gpiod_chip_get_info(chip);
|
|
if (!chip_info)
|
|
die_perror("unable to read info from chip %s",
|
|
gpiod_chip_get_path(chip));
|
|
|
|
num_lines = gpiod_chip_info_get_num_lines(chip_info);
|
|
|
|
if ((chip_num == 0) && (cfg->chip_id && !cfg->by_name))
|
|
resolve_lines_by_offset(resolver, num_lines);
|
|
|
|
for (offset = 0; ((offset < num_lines) &&
|
|
!(resolver->num_lines && resolve_done(resolver)));
|
|
offset++) {
|
|
info = gpiod_chip_get_line_info(chip, offset);
|
|
if (!info)
|
|
die_perror("unable to read info for line %d from %s",
|
|
offset, gpiod_chip_info_get_name(chip_info));
|
|
|
|
if (resolver->num_lines &&
|
|
!resolve_line(resolver, info, chip_num)) {
|
|
gpiod_line_info_free(info);
|
|
continue;
|
|
}
|
|
|
|
if (resolver->num_lines) {
|
|
printf("%s %u", gpiod_chip_info_get_name(chip_info),
|
|
offset);
|
|
} else {
|
|
if (offset == 0)
|
|
printf("%s - %u lines:\n",
|
|
gpiod_chip_info_get_name(chip_info),
|
|
num_lines);
|
|
|
|
printf("\tline %3u:", offset);
|
|
}
|
|
|
|
fputc('\t', stdout);
|
|
print_line_info(info, cfg->unquoted_strings);
|
|
fputc('\n', stdout);
|
|
gpiod_line_info_free(info);
|
|
resolver->num_found++;
|
|
}
|
|
|
|
gpiod_chip_info_free(chip_info);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
struct line_resolver *resolver = NULL;
|
|
int num_chips, i, ret = EXIT_SUCCESS;
|
|
struct gpiod_chip *chip;
|
|
struct config cfg;
|
|
char **paths;
|
|
|
|
set_prog_name(argv[0]);
|
|
i = parse_config(argc, argv, &cfg);
|
|
argc -= i;
|
|
argv += i;
|
|
|
|
if (!cfg.chip_id)
|
|
cfg.by_name = true;
|
|
|
|
num_chips = chip_paths(cfg.chip_id, &paths);
|
|
if (cfg.chip_id && (num_chips == 0))
|
|
die("cannot find GPIO chip character device '%s'", cfg.chip_id);
|
|
|
|
resolver = resolver_init(argc, argv, num_chips, cfg.strict,
|
|
cfg.by_name);
|
|
|
|
for (i = 0; i < num_chips; i++) {
|
|
chip = gpiod_chip_open(paths[i]);
|
|
if (chip) {
|
|
list_lines(resolver, chip, i, &cfg);
|
|
gpiod_chip_close(chip);
|
|
} else {
|
|
print_perror("unable to open chip '%s'", paths[i]);
|
|
|
|
if (cfg.chip_id)
|
|
return EXIT_FAILURE;
|
|
|
|
ret = EXIT_FAILURE;
|
|
}
|
|
free(paths[i]);
|
|
}
|
|
free(paths);
|
|
|
|
validate_resolution(resolver, cfg.chip_id);
|
|
if (argc && resolver->num_found != argc)
|
|
ret = EXIT_FAILURE;
|
|
free_line_resolver(resolver);
|
|
|
|
return ret;
|
|
}
|