Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

Commit

Permalink
kdb-cli: add new main.c
Browse files Browse the repository at this point in the history
and add command.h containing helpful macros and a struct def that is
needed later
  • Loading branch information
hannes99 committed Sep 21, 2022
1 parent 3e9f5ed commit a25c97c
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 263 deletions.
5 changes: 3 additions & 2 deletions src/tools/kdb/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
include (LibAddMacros)

file (GLOB HDR_FILES *.hpp gen/*.hpp gen/*/*.hpp)
file (GLOB HDR_FILES *.h gen/*.hpp gen/*/*.hpp)
add_headers (HDR_FILES)
add_cppheaders (HDR_FILES)
add_toolheaders (HDR_FILES)
include_directories (${CMAKE_CURRENT_SOURCE_DIR})

file (GLOB SRC_FILES *.cpp gen/*.cpp gen/*/*.cpp)
file (GLOB SRC_FILES *.c gen/*.cpp gen/*/*.cpp)
list (REMOVE_ITEM SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/gen/templates/collect.cpp")
set (SOURCES ${SRC_FILES} ${HDR_FILES})

Expand All @@ -28,6 +28,7 @@ if (BUILD_SHARED)
elektra-core
elektra-kdb
elektratools
elektra-invoke
elektra-opts
elektra-merge)

Expand Down
75 changes: 75 additions & 0 deletions src/tools/kdb/command.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* @file
*
* @brief Header for things used everywhere in the kdb tool
*
* @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
*/

#ifndef ELEKTRA_KDB_COMMAND_H
#define ELEKTRA_KDB_COMMAND_H

#include <kdb.h>

#define CLI_BASE_KEY "/sw/elektra/kdb/#0/current"

#define ADD_BASIC_OPTIONS(baseSpec, baseKeyName) \
ksAppendKey (baseSpec, keyNew (baseKeyName "/debug", KEY_META, "description", \
"Give debug information or ask debug questions (in interactive mode).", KEY_META, "opt", "d", \
KEY_META, "opt/long", "debug", KEY_META, "opt/arg", "none", KEY_END)); \
ksAppendKey (baseSpec, keyNew (baseKeyName "/verbose", KEY_META, "description", "Explain what is happening.", KEY_META, "opt", \
"v", KEY_META, "opt/long", "verbose", KEY_META, "opt/arg", "none", KEY_END)); \
ksAppendKey (baseSpec, keyNew (baseKeyName "/version", KEY_META, "description", "Print version info.", KEY_META, "opt", "V", \
KEY_META, "opt/long", "version", KEY_META, "opt/arg", "none", KEY_END)); \
ksAppendKey (baseSpec, keyNew (baseKeyName "/profile", KEY_META, "description", "Use a different profile for kdb configuration.", \
KEY_META, "opt", "p", KEY_META, "opt/arg/help", "NAME", KEY_META, "opt/long", "profile", KEY_META, \
"opt/arg", "required", KEY_END)); \
ksAppendKey (baseSpec, keyNew (baseKeyName "/color", KEY_META, "description", "Print never/auto(default)/always colored output.", \
KEY_META, "opt", "c", KEY_META, "opt/arg/help", "WHEN", KEY_META, "opt/long", "color", KEY_META, \
"opt/arg", "required", KEY_END)); \
ksAppendKey (baseSpec, \
keyNew (baseKeyName "/nonewline", KEY_META, "description", "Suppress the newline at the end of the output.", \
KEY_META, "opt", "n", KEY_META, "opt/long", "no-newline", KEY_META, "opt/arg", "none", KEY_END));

#define GET_OPT_KEY(options, key) ksLookupByName (options, key, 0)
#define GET_OPT(options, key) keyString (GET_OPT_KEY (options, key))

#define OR(value, def) \
void * tmp = value; \
tmp == NULL ? (def) : tmp
#define HAS_ERR(errorKey) keyGetMeta (errorKey, "error/reason") != NULL
#define GET_ERR(errorKey) keyString (keyGetMeta (errorKey, "error/reason"))
#define COMMAND_BASE_KEY(name) CLI_BASE_KEY "/" name
#define COMMAND_SPEC_KEY(name) "spec:" COMMAND_BASE_KEY (name)

#define EXEC_EXT(prog, argv, status) \
pid_t extPid; \
int timeout = 1000; \
if (0 == (extPid = fork ())) \
{ \
if (-1 == execve (prog, (char **) (argv), NULL)) \
{ \
perror ("child process execve failed [%m]"); \
return -1; \
} \
} \
while (0 == waitpid (extPid, status, WNOHANG)) \
{ \
if (--timeout < 0) \
{ \
perror ("timeout"); \
return -1; \
} \
sleep (1); \
}


typedef struct command
{
const char * name;
void (*addSpec) (KeySet * spec);
int (*exec) (KeySet * options, Key * errorKey);
} command;


#endif // ELEKTRA_KDB_COMMAND_H
138 changes: 138 additions & 0 deletions src/tools/kdb/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* @file
*
* @brief The KDB cli tool
*
* @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
*/

#include <command.h>
#include <kdb.h>
#include <kdbgopts.h>
#include <kdbhelper.h>
#include <kdbinvoke.h>
#include <stdio.h>
#include <stdlib.h>

#define CLI_SPEC_KEY "spec:" CLI_BASE_KEY

extern char ** environ;

command subcommands[] = {
{NULL, NULL, NULL},
};

void printWarnings (Key * errorKey)
{
const Key * warningsKey = keyGetMeta (errorKey, "warnings");
if (warningsKey == NULL)
{
return;
}
const char * warnings = keyString (warningsKey);
warnings = warnings[1] == '_' ? warnings + 2 : warnings + 1;

int warningsCount = atoi (warnings);
char buff[8 + 1 + 1 + 11 + 1 + 6 + 1];
for (int i = 0; i <= warningsCount; ++i)
{
snprintf (buff, sizeof buff, "warnings/#%d/reason", i);
const char * warning = keyString (keyGetMeta (errorKey, buff));
printf ("WARNING: %s\n", warning);
}
}

int main (int argc, char ** argv)
{
if (argc == 2 && elektraStrCmp (argv[1], "--elektra-spec") == 0)
{
Key * parentKey = keyNew (CLI_SPEC_KEY, KEY_END);
KeySet * options = ksNew (1,
keyNew (CLI_SPEC_KEY, KEY_META, "command", "", KEY_META, "description",
"kdb is a program to manage Elektra's key database.", KEY_END),
KS_END);

for (unsigned long i = 0; i < sizeof (subcommands) / sizeof (subcommands[0]); ++i)
{
subcommands[i].addSpec (options);
}

KeySet * specloadConf = ksNew (1, keyNew ("system:/sendspec", KEY_END), KS_END);
ElektraInvokeHandle * specload = elektraInvokeOpen ("specload", specloadConf, parentKey);
int result = elektraInvoke2Args (specload, "sendspec", options, parentKey);

elektraInvokeClose (specload, parentKey);
keyDel (parentKey);
ksDel (specloadConf);
return result;
}

Key * parentKey = keyNew (CLI_BASE_KEY, KEY_END);
KeySet * options = ksNew (0, KS_END);

KeySet * goptsConfig = ksNew (0, KS_END);
KeySet * contract = ksNew (0, KS_END);

elektraGOptsContract (contract, argc, (const char * const *) argv, (const char * const *) environ, parentKey, goptsConfig);

KDB * kdb = kdbOpen (contract, parentKey);

ksDel (goptsConfig);
ksDel (contract);

int result = kdbGet (kdb, options, parentKey);

const char * subcommand = keyString (ksLookupByName (options, CLI_BASE_KEY, 0));
if (elektraStrCmp (subcommand, "") == 0)
{
printf ("kdb is a program to manage Elektra's key database. For more information use --help.\n\n");
kdbClose (kdb, parentKey);
keyDel (parentKey);
ksDel (options);
return EXIT_SUCCESS;
}

Key * helpKey = ksLookupByName (options, "proc:/elektra/gopts/help", 0);
if (helpKey != NULL && elektraStrCmp (keyString (helpKey), "1") == 0)
{
const char * help = keyString (ksLookupByName (options, "proc:/elektra/gopts/help/message", 0));
printf ("%s\n", help);
kdbClose (kdb, parentKey);
keyDel (parentKey);
ksDel (options);
return EXIT_SUCCESS;
}

if (result == -1)
{
fprintf (stderr, "ERROR: %s\n", GET_ERR (parentKey));
kdbClose (kdb, parentKey);
keyDel (parentKey);
ksDel (options);
return EXIT_FAILURE;
}

keyDel (parentKey);
for (unsigned long i = 0; i < sizeof (subcommands) / sizeof (subcommands[0]); ++i)
{
if (elektraStrCmp (subcommand, subcommands[i].name) == 0)
{
parentKey = keyNew (CLI_BASE_KEY, KEY_END);
result = subcommands[i].exec (options, parentKey);
if (HAS_ERR (parentKey))
{
fprintf (stderr, "ERROR: %s\n", GET_ERR (parentKey));
}
printWarnings (parentKey);
kdbClose (kdb, parentKey);
keyDel (parentKey);
ksDel (options);
return result;
}
}

kdbClose (kdb, parentKey);
keyDel (parentKey);
ksDel (options);
return 0;
}
Loading

0 comments on commit a25c97c

Please sign in to comment.