Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opts plugin #2471

Merged
merged 41 commits into from Apr 2, 2019
Merged

Opts plugin #2471

Changes from 39 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
cb4b2a1
gopts: global opts plugin basics
kodebach Feb 12, 2019
ed5c798
gopts: basic implementations
kodebach Mar 3, 2019
4e4a5af
gopts: finish implementation
kodebach Mar 8, 2019
19b420d
gopts: documentation
kodebach Mar 8, 2019
da908a0
gopts: fix win32
kodebach Mar 8, 2019
f9860a6
gopts: fix unitialized error
kodebach Mar 10, 2019
4c4d812
gopts: fix formatting
kodebach Mar 10, 2019
c46fc89
gopts: fix formatting
kodebach Mar 10, 2019
f77c480
gopts: fix memleak
kodebach Mar 12, 2019
b5f98ff
gopts: incorporate review
kodebach Mar 12, 2019
6ca1fb1
gopts: documentation
kodebach Mar 12, 2019
7bc960d
gopts: fix formatting
kodebach Mar 12, 2019
aeb860d
gopts: fix LD_LIBRARY_PATH
kodebach Mar 12, 2019
197c785
gopts: improve test error
kodebach Mar 14, 2019
4f24207
mathcheck: fix shell test
kodebach Mar 21, 2019
d8faf43
kdb: fix kdbGet global postgetstorage parent key
kodebach Mar 21, 2019
ef896ca
gopts: fix test
kodebach Mar 21, 2019
4448503
gopts: fix testapp
kodebach Mar 21, 2019
79ceb9e
multifile: fix shell test
kodebach Mar 21, 2019
24a5885
kdb: add procgetstorage global position
kodebach Mar 21, 2019
dfb41d6
kdb/gopts: add gopts as global procstorage plugin by default
kodebach Mar 22, 2019
8925478
list: fix placements
kodebach Mar 22, 2019
de7bab3
tests: fix testshell_markdown_tutorial_validation
kodebach Mar 22, 2019
74b6308
kdb: fix global mount config
kodebach Mar 24, 2019
71bacc8
kdb: kdbEnsure
kodebach Mar 27, 2019
08f0cef
kdb: remove gopts from default global plugins
kodebach Mar 27, 2019
65130e5
kdb: fix tests
kodebach Mar 27, 2019
cb74c10
kdb: add tests for kdbEnsure and fix bugs
kodebach Mar 27, 2019
aa7e40f
notification: use kdbEnsure in libnotification
kodebach Mar 27, 2019
7370a5f
kdb: fix tests
kodebach Mar 27, 2019
844f566
kdb: fix tests
kodebach Mar 28, 2019
b81e6a0
kdb: fix icheck
kodebach Mar 28, 2019
c79c67d
kdb: fix memleak
kodebach Mar 28, 2019
9f72f93
kdb: fix missing return
kodebach Mar 28, 2019
41ee812
kdb: rename elektraFindGlobalPlugin to elektraPluginFindGlobal
kodebach Mar 28, 2019
71076e4
doc: update documentation
kodebach Mar 28, 2019
a5dbd40
kdb: changes from reviews
kodebach Mar 31, 2019
31e9534
gopts: add example and fix problems
kodebach Mar 31, 2019
6767523
cmake: fix cmake_policy
kodebach Mar 31, 2019
f078edf
opts: review improvements
kodebach Apr 1, 2019
d4fe121
opts: fix formatting
kodebach Apr 1, 2019
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.
+2,570 −771
Diff settings

Always

Just for now

@@ -12,7 +12,7 @@ function (add_lib name)

if (BUILD_SHARED)
add_library (elektra-${name} SHARED ${ARG_SOURCES})
add_dependencies (elektra-${name} kdberrors_generated elektra_error_codes_generated)
add_dependencies (elektra-${name} kdberrors_generated elektra_error_codes_generated ${ARG_LINK_ELEKTRA})

target_link_libraries (elektra-${name} elektra-core ${ARG_LINK_ELEKTRA})
endif (BUILD_SHARED)
@@ -51,7 +51,24 @@ the `type` plugin will ignore the key. We now also support converting enum value
To switch from `boolean` to the new `type`, you don't have to do anything, if you used the default config. If you used a custom configuration
please take a look at the [README](https://www.libelektra.org/plugins/type).

### <<HIGHLIGHT2>>
### kdbEnsure

`kdbEnsure` is a new function in `elektra-kdb`. It can be used to ensure that a KDB instance meets certain clauses specified in a
contract. In principle this a very powerful tool that may be used for a lot of things. For now it only supports a few clauses concerning
plugins:

- You can specify that a plugin should be mounted globally. This can for example be used to enable the new [gopts](#gopts) plugin.
- Conversely you can also define that a plugin should not be mounted globally, e.g. to disable the `spec` plugin, which is enabled by default.
- Additionally you may want to enforce that a global plugin uses a certain configuration. For this case you can specify that the plugin
should be remounted, i.e. unmounted and immediately mounted again.
- Because of the different architecture involved, for now only unmounting of non-global plugins is supported.

All changes made by `kdbEnsure` are purely temporary. They will only apply to the KDB handle passed to the function.

IMPORTANT: `kdbEnsure` only works, if the `list` plugin is mounted in all appropriate global positions.

Note: `kdbEnsure` right now ignores the `infos/recommends` and `infos/needs` metadata of plugins, so you have to explicitly take care of
dependencies. _(Klemens Böswirth)_

### <<HIGHLIGHT2>>

@@ -155,6 +172,15 @@ The following section lists news about the [modules](https://www.libelektra.org/

- We fixed an incorrect format specifier in a call to the `syslog` function. _(René Schwaiger)_

### gOpts

- The [gopts](https://www.libelektra.org/plugins/gopts) plugin simply retrieves the values of `argc`, `argv` and `envp` needed for
[`elektraGetOpts`](https://www.libelektra.org/tutorials/command-line-options) and then makes the call. It is intended to be used as a
global plugin, so that command-line options are automatically parsed when `kdbGet` is called. _(Klemens Böswirth)_
- The plugin works under WIN32 (via `GetCommandLineW` and `GetEnvironmentString`), MAC_OSX (`_NSGetArgc`, `_NSGetArgv`) and any system that
either has a `sysctl(3)` function that accepts `KERN_PROC_ARGS` (e.g. FreeBSD) or when `procfs` is mounted and either `/proc/self` or
`/proc/curproc` refers to the current process. If you need support for any other systems, feel free to add an implementation.

## Libraries

The text below summarizes updates to the [C (and C++)-based libraries](https://www.libelektra.org/libraries/readme) of Elektra.
@@ -171,7 +197,7 @@ compiled against an older 0.8 version of Elektra will continue to work

### Core

- <<TODO>>
- `kdbGet` now calls global postgetstorage plugins with the parent key passed to `kdbGet`, instead of a random mountpoint. _(Klemens Böswirth)_

This comment has been minimized.

Copy link
@mpranj

This comment has been minimized.

Copy link
@kodebach

kodebach Mar 30, 2019

Author Contributor

One thing I noticed (but didn't look into): At least some errors returned from global plugins are suppressed by kdbGet. You may want to look into that while working on #2457.

This comment has been minimized.

Copy link
@mpranj

mpranj Mar 30, 2019

Member

Yep, we didn't even use return values until #2307. The global plugin implementation needs to be re-worked and tested properly, so that it matches the proposal.

- <<TODO>>
- <<TODO>>

@@ -318,6 +318,7 @@ Before we look further let us undo the modifications to the key database.
```sh
kdb rm -r spec/tests/tutorial
kdb rm -r system/tests/tutorial
kdb rm -rf user/tests/tutorial
kdb umount spec/tests/tutorial
kdb umount /tests/tutorial
kdb rm -rf spec
@@ -34,8 +34,9 @@ target_link_elektra (kdbintro elektra-kdb)
target_link_elektra (kdbopen elektra-kdb)
target_link_elektra (kdbset elektra-kdb)
target_link_elektra (set_key elektra-kdb)
target_link_elektra (opts elektra-opts)
target_link_elektra (opts elektra-ease elektra-opts)
target_link_elektra (optsSnippets elektra-opts)
target_link_elektra (gopts elektra-ease elektra-opts elektra-kdb)

# Notification examples

@@ -0,0 +1,241 @@
/**
* @file
*
* @brief
*
* @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
*/

#include <kdb.h>
#include <kdbease.h>
#include <kdbhelper.h>

#include <kdbopts.h>
#include <stdio.h>
#include <stdlib.h>

extern char ** environ;

#define BASE_KEY "/sw/org/erm/#0/current"
#define SPEC_BASE_KEY "spec" BASE_KEY

static KeySet * createSpec (void)
{
return ksNew (
10,
keyNew (SPEC_BASE_KEY "/emptydirs", KEY_META, "description", "remove empty directories", KEY_META, "opt", "d", KEY_META,
"opt/arg", "none", KEY_META, "opt/long", "dir", KEY_END),
keyNew (SPEC_BASE_KEY "/force", KEY_META, "description", "ignore nonexistent files and arguments, never prompt", KEY_META,
"opt", "f", KEY_META, "opt/arg", "none", KEY_META, "opt/long", "force", KEY_END),
keyNew (SPEC_BASE_KEY "/interactive", KEY_META, "description",
"prompt according to WHEN: never, once (-I), or always (-i), without WHEN, prompt always", KEY_META, "opt", "#1",
KEY_META, "opt/#0", "i", KEY_META, "opt/#0/arg", "optional", KEY_META, "opt/#0/flagvalue", "always", KEY_META,
"opt/#0/long", "interactive", KEY_META, "opt/#1", "I", KEY_META, "opt/#1/arg", "none", KEY_META, "opt/#1/flagvalue",
"once", KEY_META, "opt/arg/name", "WHEN", KEY_END),
keyNew (SPEC_BASE_KEY "/nopreserve", KEY_META, "description", "do not treat '/' specially", KEY_META, "opt/arg", "none",
KEY_META, "opt/long", "no-preserve-root", KEY_END),
keyNew (SPEC_BASE_KEY "/preserve", KEY_META, "description",
"do not remove '/' (default), with 'all', reject any command line argument on a separate device from its parent",
KEY_META, "opt/arg", "optional", KEY_META, "opt/arg/name", "all", KEY_META, "opt/flagvalue", "root", KEY_META,
"opt/long", "preserve-root", KEY_END),
keyNew (SPEC_BASE_KEY "/recursive", KEY_META, "description", "remove directories and their contents recursively", KEY_META,
"opt", "#1", KEY_META, "opt/#0", "r", KEY_META, "opt/#0/arg", "none", KEY_META, "opt/#0/long", "recursive",
KEY_META, "opt/#1", "R", KEY_META, "opt/#1/arg", "none", KEY_END),
keyNew (SPEC_BASE_KEY "/showversion", KEY_META, "description", "output version information and exit", KEY_META, "opt/arg",
"none", KEY_META, "opt/long", "version", KEY_END),
keyNew (SPEC_BASE_KEY "/singlefs", KEY_META, "description",
"when removing a hierarchy recursively, skip any directory that is on a file system different from that of the "
"corresponding line argument",
KEY_META, "opt/arg", "none", KEY_META, "opt/long", "one-file-system", KEY_END),
keyNew (SPEC_BASE_KEY "/verbose", KEY_META, "description", "explain what is being done", KEY_META, "opt", "v", KEY_META,
"opt/arg", "none", KEY_META, "opt/long", "verbose", KEY_META, "env", "VERBOSE", KEY_END),
keyNew (SPEC_BASE_KEY "/files/#", KEY_META, "description", "the files that shall be deleted", KEY_META, "args", "remaining",
KEY_META, "env", "FILES", KEY_END),
KS_END);
}

static int setupSpec (void)

This comment has been minimized.

Copy link
@markus2330

markus2330 Apr 1, 2019

Contributor

Please mention here that this is only like this to be self-contained, not that an application should do this.

{
Key * parentKey = keyNew (SPEC_BASE_KEY, KEY_END);
KDB * kdb = kdbOpen (parentKey);
KeySet * ks = ksNew (0, KS_END);
kdbGet (kdb, ks, parentKey);

KeySet * existing = ksCut (ks, parentKey);
if (ksGetSize (existing) > 0)
{
kdbClose (kdb, parentKey);
ksDel (ks);
ksDel (existing);
return 0;
}
ksDel (existing);

KeySet * spec = createSpec ();
ksAppend (ks, spec);
ksDel (spec);
kdbSet (kdb, ks, parentKey);
kdbClose (kdb, parentKey);
ksDel (ks);

return 1;
}

static void removeSpec (void)
{
Key * parentKey = keyNew (SPEC_BASE_KEY, KEY_END);
KDB * kdb = kdbOpen (parentKey);
KeySet * ks = ksNew (0, KS_END);
kdbGet (kdb, ks, parentKey);
KeySet * spec = ksCut (ks, parentKey);
ksDel (spec);
kdbSet (kdb, ks, parentKey);
kdbClose (kdb, parentKey);
ksDel (ks);
}

int main (void)
{
if (!setupSpec ())
{
fprintf (stderr, "ERROR: Couldn't setup spec, keys exist!\n");
return EXIT_FAILURE;
}

Key * parentKey = keyNew (BASE_KEY, KEY_END);
KDB * kdb = kdbOpen (parentKey);

KeySet * contract = ksNew (1, keyNew ("system/plugins/global/gopts", KEY_VALUE, "mounted", KEY_END), KS_END);
int rc = kdbEnsure (kdb, contract, parentKey);
if (rc == 1)
{
fprintf (stderr, "ERROR: Contract could not be ensured!\n%s\n", keyString (keyGetMeta (parentKey, "error/reason")));
kdbClose (kdb, parentKey);
keyDel (parentKey);
removeSpec ();
return EXIT_FAILURE;
}
else if (rc == -1)
{
fprintf (stderr, "ERROR: Contract malformed/NULL pointer!\n%s\n", keyString (keyGetMeta (parentKey, "error/reason")));
kdbClose (kdb, parentKey);
keyDel (parentKey);
removeSpec ();
return EXIT_FAILURE;
}

KeySet * ks = ksNew (0, KS_END);
rc = kdbGet (kdb, ks, parentKey);

if (rc == -1)
{
fprintf (stderr, "ERROR: kdbGet failed! %s\n", keyString (keyGetMeta (parentKey, "error/reason")));
kdbClose (kdb, parentKey);
keyDel (parentKey);
ksDel (ks);
removeSpec ();
return EXIT_FAILURE;
}

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

printf ("When called with the same arguments 'rm' \n");

Key * lookup = ksLookupByName (ks, BASE_KEY "/emptydirs", 0);
if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
{
printf ("will delete empty directories\n");
}

lookup = ksLookupByName (ks, BASE_KEY "/force", 0);
if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
{
printf ("will use force mode\n");
}

lookup = ksLookupByName (ks, BASE_KEY "/interactive", 0);
if (lookup != NULL && elektraStrCmp (keyString (lookup), "never") == 0)
{
printf ("will not use interactive mode\n");
}
else if (lookup != NULL && elektraStrCmp (keyString (lookup), "once") == 0)
{
printf ("will use interactive mode; ask once\n");
}
else if (lookup != NULL && elektraStrCmp (keyString (lookup), "always") == 0)
{
printf ("will use interactive mode; always ask\n");
}

lookup = ksLookupByName (ks, BASE_KEY "/nopreserve", 0);
if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
{
printf ("will not treat '/' specially\n");
}

lookup = ksLookupByName (ks, BASE_KEY "/preserve", 0);
if (lookup != NULL && elektraStrCmp (keyString (lookup), "root") == 0)
{
printf ("will never remove '/'");
}
else if (lookup != NULL && elektraStrCmp (keyString (lookup), "all") == 0)
{
printf ("will reject any argument on separate device from its parent\n");
}

lookup = ksLookupByName (ks, BASE_KEY "/recursive", 0);
if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
{
printf ("will delete recursively\n");
}

lookup = ksLookupByName (ks, BASE_KEY "/showversion", 0);
if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
{
printf ("will show version and exit\n");
}

lookup = ksLookupByName (ks, BASE_KEY "/singlefs", 0);
if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
{
printf ("will skip directories on different file systems\n");
}

lookup = ksLookupByName (ks, BASE_KEY "/verbose", 0);
if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
{
printf ("will explain what is being done\n");
}

printf ("will remove the following files:\n");

Key * arrayParent = ksLookupByName (ks, BASE_KEY "/files", 0);
KeySet * files = elektraArrayGet (arrayParent, ks);

ksRewind (files);
Key * cur = NULL;
while ((cur = ksNext (files)) != NULL)
{
printf (" %s\n", keyString (cur));
}
printf ("\n");

kdbClose (kdb, parentKey);
keyDel (parentKey);
ksDel (ks);

removeSpec ();

return EXIT_SUCCESS;
}
@@ -21,7 +21,7 @@ extern char ** environ;
static KeySet * createSpec (void)
{
return ksNew (
9,
10,
keyNew (SPEC_BASE_KEY "/emptydirs", KEY_META, "description", "remove empty directories", KEY_META, "opt", "d", KEY_META,
"opt/arg", "none", KEY_META, "opt/long", "dir", KEY_END),
keyNew (SPEC_BASE_KEY "/force", KEY_META, "description", "ignore nonexistent files and arguments, never prompt", KEY_META,
@@ -121,9 +121,29 @@ class KDBException : public Exception
return "User Exception: KDB";
}

private:
protected:
Key m_key;
};

class ContractException : public KDBException
{
public:
explicit ContractException (Key key) : KDBException (key)
{
}

~ContractException () noexcept override = default;

const char * what () const noexcept override
{
if (!m_key)
{
return "Malformed contract";
}
return KDBException::what ();
}
};

} // namespace kdb


Oops, something went wrong.
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.