*/
-#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <libgen.h>
#include <dirent.h>
+#include <signal.h>
#include <sys/queue.h>
#include "client.h"
/* Global for completion */
static struct cmd_node *root = NULL;
+const char *ctlname = NULL;
static int
is_lldpctl(const char *name)
fprintf(stderr, "\n");
fprintf(stderr, "-d Enable more debugging information.\n");
- fprintf(stderr, "-f format Choose output format (plain, keyvalue or xml).\n");
+ fprintf(stderr, "-u socket Specify the Unix-domain socket used for communication with lldpd(8).\n");
+ fprintf(stderr, "-f format Choose output format (plain, keyvalue, json, json0"
+#if defined USE_XML
+ ", xml"
+#endif
+ ").\n");
if (!is_lldpctl(NULL))
- fprintf(stderr, "-c Read the provided configuration file.\n");
+ fprintf(stderr, "-c conf Read the provided configuration file.\n");
fprintf(stderr, "\n");
static int
is_privileged()
{
- return (!(getuid() != geteuid() || getgid() != getegid()));
+ /* Check we can access the control socket with read/write
+ * privileges. The `access()` function uses the real UID and real GID,
+ * therefore we don't have to mangle with our identity. */
+ return (ctlname && access(ctlname, R_OK|W_OK) == 0);
}
static char*
lldpctl_atom_dec_ref(config);
return 0;
}
- log_info("lldpctl", "immediate retransmission requested successfuly");
+ log_info("lldpctl", "immediate retransmission requested successfully");
lldpctl_atom_dec_ref(config);
return 1;
}
return 0;
}
if (lldpctl_atom_get_int(config, lldpctl_k_config_paused) == pause) {
- log_info("lldpctl", "lldpd is already %s",
+ log_debug("lldpctl", "lldpd is already %s",
pause?"paused":"resumed");
lldpctl_atom_dec_ref(config);
- return 0;
+ return 1;
}
if (lldpctl_atom_set_int(config,
lldpctl_k_config_paused, pause) == NULL) {
char **argv = NULL;
int argc = 0;
int rc = 1;
- char *line = malloc(strlen(rl_line_buffer) + 2);
+ size_t len = strlen(rl_line_buffer);
+ char *line = malloc(len + 2);
if (!line) return -1;
- strcpy(line, rl_line_buffer);
+ strlcpy(line, rl_line_buffer, len + 2);
line[rl_point] = 2; /* empty character, will force a word */
line[rl_point+1] = 0;
if (tokenize_line(line, &argc, &argv) != 0)
goto end;
- char *compl = commands_complete(root, argc, (const char **)argv, all);
+ char *compl = commands_complete(root, argc, (const char **)argv, all, is_privileged());
if (compl && strlen(argv[argc-1]) < strlen(compl)) {
if (rl_insert_text(compl + strlen(argv[argc-1])) < 0) {
free(compl);
goto end;
}
/* No completion or several completion available. */
+ free(compl);
fprintf(stderr, "\n");
rl_forced_update_display();
rc = 0;
}
#else
static char*
-readline()
+readline(const char *p)
{
static char line[2048];
- fprintf(stderr, "%s", prompt());
+ fprintf(stderr, "%s", p);
fflush(stderr);
if (fgets(line, sizeof(line) - 2, stdin) == NULL)
return NULL;
- return line;
+ return strdup(line);
}
#endif
if (strcmp(fmt, "plain") == 0) w = txt_init(stdout);
else if (strcmp(fmt, "keyvalue") == 0) w = kv_init(stdout);
+ else if (strcmp(fmt, "json") == 0) w = json_init(stdout, 1);
+ else if (strcmp(fmt, "json0") == 0) w = json_init(stdout, 0);
#ifdef USE_XML
else if (strcmp(fmt, "xml") == 0) w = xml_init(stdout);
#endif
-#ifdef USE_JSON
- else if (strcmp(fmt, "json") == 0) w = json_init(stdout);
-#endif
- else w = txt_init(stdout);
+ else {
+ log_warnx("lldpctl", "unknown output format \"%s\"", fmt);
+ w = txt_init(stdout);
+ }
/* Execute command */
int rc = commands_execute(conn, w,
- root, argc, argv);
+ root, argc, argv, is_privileged());
if (rc != 0) {
log_info("lldpctl", "an error occurred while executing last command");
w->finish(w);
log_warnx("lldpctl", "unmatched quotes");
return -1;
}
- if (cargc == 0) return 0;
- n = cmd_exec(conn, fmt, cargc, (const char **)cargv);
+ if (cargc != 0)
+ n = cmd_exec(conn, fmt, cargc, (const char **)cargv);
tokenize_free(cargc, cargv);
- return (n == 0)?-1:1;
+ return (cargc == 0)?0:
+ (n == 0)?-1:
+ 1;
}
static struct cmd_node*
root = commands_root();
register_commands_show(root);
register_commands_watch(root);
- if (is_privileged()) {
- commands_new(
- commands_new(root, "update", "Update information and send LLDPU on all ports",
- NULL, NULL, NULL),
- NEWLINE, "Update information and send LLDPU on all ports",
- NULL, cmd_update, NULL);
- register_commands_configure(root);
- }
+ commands_privileged(commands_new(
+ commands_new(root, "update", "Update information and send LLDPU on all ports",
+ NULL, NULL, NULL),
+ NEWLINE, "Update information and send LLDPU on all ports",
+ NULL, cmd_update, NULL));
+ register_commands_configure(root);
+ commands_hidden(commands_new(root, "complete", "Get possible completions from a given command",
+ NULL, cmd_store_env_and_pop, "complete"));
commands_new(root, "help", "Get help on a possible command",
NULL, cmd_store_env_and_pop, "help");
commands_new(
* @param acceptdir 1 if we accept a directory, 0 otherwise
*/
static void
-input_append(const char *arg, struct inputs *inputs, int acceptdir)
+input_append(const char *arg, struct inputs *inputs, int acceptdir, int warn)
{
struct stat statbuf;
if (stat(arg, &statbuf) == -1) {
- log_info("lldpctl", "cannot find configuration file/directory %s",
- arg);
+ if (warn) {
+ log_warn("lldpctl", "cannot find configuration file/directory %s",
+ arg);
+ } else {
+ log_debug("lldpctl", "cannot find configuration file/directory %s",
+ arg);
+ }
return;
}
for (int i=0; i < n; i++) {
char *fullname;
if (asprintf(&fullname, "%s/%s", arg, namelist[i]->d_name) != -1) {
- input_append(fullname, inputs, 0);
+ input_append(fullname, inputs, 0, 1);
free(fullname);
}
free(namelist[i]);
int
main(int argc, char *argv[])
{
- int ch, debug = 1, rc = EXIT_FAILURE;
+ int ch, debug = 0, use_syslog = 0, rc = EXIT_FAILURE;
const char *fmt = "plain";
lldpctl_conn_t *conn = NULL;
- const char *options = is_lldpctl(argv[0])?"hdvf:":"hdsvf:c:";
+ const char *options = is_lldpctl(argv[0])?"hdvf:u:":"hdsvf:c:C:u:";
- int gotinputs = 0;
+ int gotinputs = 0, version = 0;
struct inputs inputs;
TAILQ_INIT(&inputs);
- /* Initialize logging */
- while ((ch = getopt(argc, argv, options)) != -1) {
- switch (ch) {
- case 'd': debug++; break;
- case 's': debug--; break;
- }
- }
- log_init(debug, __progname);
+ ctlname = lldpctl_get_default_transport();
+
+ signal(SIGHUP, SIG_IGN);
/* Get and parse command line options */
optind = 1;
while ((ch = getopt(argc, argv, options)) != -1) {
switch (ch) {
- case 'd': break;
- case 's': break;
+ case 'd':
+ if (use_syslog)
+ use_syslog = 0;
+ else
+ debug++;
+ break;
+ case 's':
+ if (debug == 0)
+ use_syslog = 1;
+ else
+ debug--;
+ break;
case 'h':
usage();
break;
+ case 'u':
+ ctlname = optarg;
+ break;
case 'v':
- fprintf(stdout, "%s\n", PACKAGE_VERSION);
- exit(0);
+ version++;
break;
case 'f':
fmt = optarg;
break;
+ case 'C':
case 'c':
- gotinputs = 1;
- input_append(optarg, &inputs, 1);
+ if (!gotinputs) {
+ log_init(use_syslog, debug, __progname);
+ lldpctl_log_level(debug + 1);
+ gotinputs = 1;
+ }
+ input_append(optarg, &inputs, 1, ch == 'c');
break;
default:
usage();
}
}
+ if (version) {
+ version_display(stdout, "lldpcli", version > 1);
+ exit(0);
+ }
+
+ if (!gotinputs) {
+ log_init(use_syslog, debug, __progname);
+ lldpctl_log_level(debug + 1);
+ }
+
+ /* Disable SIGPIPE */
+ signal(SIGPIPE, SIG_IGN);
+
/* Register commands */
root = register_commands();
/* Make a connection */
log_debug("lldpctl", "connect to lldpd");
- conn = lldpctl_new(NULL, NULL, NULL);
+ conn = lldpctl_new_name(ctlname, NULL, NULL, NULL);
if (conn == NULL) goto end;
/* Process file inputs */
while (gotinputs && !TAILQ_EMPTY(&inputs)) {
+ /* coverity[use_after_free]
+ TAILQ_REMOVE does the right thing */
struct input *first = TAILQ_FIRST(&inputs);
log_debug("lldpctl", "process: %s", first->name);
FILE *file = fopen(first->name, "r");
if (file) {
- size_t len;
+ size_t n;
+ ssize_t len;
char *line;
- while ((line = fgetln(file, &len))) {
+ while (line = NULL, len = 0, (len = getline(&line, &n, file)) > 0) {
if (line[len - 1] == '\n') {
line[len - 1] = '\0';
parse_and_exec(conn, fmt, line);
}
free(line);
}
+ free(line);
fclose(file);
} else {
log_warn("lldpctl", "unable to open %s",
int cargc;
cargv = &((const char **)argv)[optind];
cargc = argc - optind;
- if (cmd_exec(conn, fmt, cargc, cargv) != -1)
+ if (cmd_exec(conn, fmt, cargc, cargv) == 1)
rc = EXIT_SUCCESS;
goto end;
}
- if (gotinputs) goto end;
+ if (gotinputs) {
+ rc = EXIT_SUCCESS;
+ goto end;
+ }
/* Interactive session */
#ifdef HAVE_LIBREADLINE
rl_bind_key('?', cmd_help);
rl_bind_key('\t', cmd_complete);
#endif
- const char *line;
+ char *line = NULL;
do {
if ((line = readline(prompt()))) {
int n = parse_and_exec(conn, fmt, line);
- (void)n;
+ if (n != 0) {
#ifdef HAVE_READLINE_HISTORY
- if (n != 0) add_history(line);
+ add_history(line);
#endif
+ }
+ free(line);
}
- } while (line != NULL);
+ } while (!must_exit && line != NULL);
rc = EXIT_SUCCESS;
end:
while (!TAILQ_EMPTY(&inputs)) {
+ /* coverity[use_after_free]
+ TAILQ_REMOVE does the right thing */
struct input *first = TAILQ_FIRST(&inputs);
TAILQ_REMOVE(&inputs, first, next);
free(first->name);