Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #include <kdb.h>
10 : #include <kdbease.h>
11 : #include <kdbhelper.h>
12 :
13 : #include <kdbopts.h>
14 : #include <stdio.h>
15 : #include <stdlib.h>
16 :
17 : extern char ** environ;
18 :
19 : #define BASE_KEY "/sw/org/erm/#0/current"
20 : #define SPEC_BASE_KEY "spec" BASE_KEY
21 :
22 : // -----------------
23 : // Helper methods
24 : // -----------------
25 :
26 : /*
27 : * The methods below are only used, so that this example is self-contained.
28 : * If you actually develop an application, you may use the `specload` plugin,
29 : * but in any case the specification should be mounted into the KDB using `kdb mount`.
30 : *
31 : * DO NOT set/unset the specification inside of your application.
32 : */
33 :
34 0 : static KeySet * createSpec (void)
35 : {
36 0 : return ksNew (
37 : 10,
38 : keyNew (SPEC_BASE_KEY "/emptydirs", KEY_META, "description", "remove empty directories", KEY_META, "opt", "d", KEY_META,
39 : "opt/arg", "none", KEY_META, "opt/long", "dir", KEY_END),
40 : keyNew (SPEC_BASE_KEY "/force", KEY_META, "description", "ignore nonexistent files and arguments, never prompt", KEY_META,
41 : "opt", "f", KEY_META, "opt/arg", "none", KEY_META, "opt/long", "force", KEY_END),
42 : keyNew (SPEC_BASE_KEY "/interactive", KEY_META, "description",
43 : "prompt according to WHEN: never, once (-I), or always (-i), without WHEN, prompt always", KEY_META, "opt", "#1",
44 : KEY_META, "opt/#0", "i", KEY_META, "opt/#0/arg", "optional", KEY_META, "opt/#0/flagvalue", "always", KEY_META,
45 : "opt/#0/long", "interactive", KEY_META, "opt/#1", "I", KEY_META, "opt/#1/arg", "none", KEY_META, "opt/#1/flagvalue",
46 : "once", KEY_META, "opt/arg/name", "WHEN", KEY_END),
47 : keyNew (SPEC_BASE_KEY "/nopreserve", KEY_META, "description", "do not treat '/' specially", KEY_META, "opt/arg", "none",
48 : KEY_META, "opt/long", "no-preserve-root", KEY_END),
49 : keyNew (SPEC_BASE_KEY "/preserve", KEY_META, "description",
50 : "do not remove '/' (default), with 'all', reject any command line argument on a separate device from its parent",
51 : KEY_META, "opt/arg", "optional", KEY_META, "opt/arg/name", "all", KEY_META, "opt/flagvalue", "root", KEY_META,
52 : "opt/long", "preserve-root", KEY_END),
53 : keyNew (SPEC_BASE_KEY "/recursive", KEY_META, "description", "remove directories and their contents recursively", KEY_META,
54 : "opt", "#1", KEY_META, "opt/#0", "r", KEY_META, "opt/#0/arg", "none", KEY_META, "opt/#0/long", "recursive",
55 : KEY_META, "opt/#1", "R", KEY_META, "opt/#1/arg", "none", KEY_END),
56 : keyNew (SPEC_BASE_KEY "/showversion", KEY_META, "description", "output version information and exit", KEY_META, "opt/arg",
57 : "none", KEY_META, "opt/long", "version", KEY_END),
58 : keyNew (SPEC_BASE_KEY "/singlefs", KEY_META, "description",
59 : "when removing a hierarchy recursively, skip any directory that is on a file system different from that of the "
60 : "corresponding line argument",
61 : KEY_META, "opt/arg", "none", KEY_META, "opt/long", "one-file-system", KEY_END),
62 : keyNew (SPEC_BASE_KEY "/verbose", KEY_META, "description", "explain what is being done", KEY_META, "opt", "v", KEY_META,
63 : "opt/arg", "none", KEY_META, "opt/long", "verbose", KEY_META, "env", "VERBOSE", KEY_END),
64 : keyNew (SPEC_BASE_KEY "/files/#", KEY_META, "description", "the files that shall be deleted", KEY_META, "args", "remaining",
65 : KEY_META, "env", "FILES", KEY_END),
66 : KS_END);
67 : }
68 :
69 0 : static int setupSpec (void)
70 : {
71 0 : Key * parentKey = keyNew (SPEC_BASE_KEY, KEY_END);
72 0 : KDB * kdb = kdbOpen (parentKey);
73 0 : KeySet * ks = ksNew (0, KS_END);
74 0 : kdbGet (kdb, ks, parentKey);
75 :
76 0 : KeySet * existing = ksCut (ks, parentKey);
77 0 : if (ksGetSize (existing) > 0)
78 : {
79 0 : kdbClose (kdb, parentKey);
80 0 : ksDel (ks);
81 0 : ksDel (existing);
82 0 : return 0;
83 : }
84 0 : ksDel (existing);
85 :
86 0 : KeySet * spec = createSpec ();
87 0 : ksAppend (ks, spec);
88 0 : ksDel (spec);
89 0 : kdbSet (kdb, ks, parentKey);
90 0 : kdbClose (kdb, parentKey);
91 0 : ksDel (ks);
92 :
93 0 : return 1;
94 : }
95 :
96 0 : static void removeSpec (void)
97 : {
98 0 : Key * parentKey = keyNew (SPEC_BASE_KEY, KEY_END);
99 0 : KDB * kdb = kdbOpen (parentKey);
100 0 : KeySet * ks = ksNew (0, KS_END);
101 0 : kdbGet (kdb, ks, parentKey);
102 0 : KeySet * spec = ksCut (ks, parentKey);
103 0 : ksDel (spec);
104 0 : kdbSet (kdb, ks, parentKey);
105 0 : kdbClose (kdb, parentKey);
106 0 : ksDel (ks);
107 0 : }
108 :
109 : // -----------------
110 : // Main example
111 : // -----------------
112 :
113 0 : int main (void)
114 : {
115 0 : if (!setupSpec ())
116 : {
117 0 : fprintf (stderr, "ERROR: Couldn't setup spec, keys exist!\n");
118 0 : return EXIT_FAILURE;
119 : }
120 :
121 0 : Key * parentKey = keyNew (BASE_KEY, KEY_END);
122 0 : KDB * kdb = kdbOpen (parentKey);
123 :
124 0 : KeySet * contract = ksNew (1, keyNew ("system/elektra/ensure/plugins/global/gopts", KEY_VALUE, "mounted", KEY_END), KS_END);
125 0 : int rc = kdbEnsure (kdb, contract, parentKey);
126 0 : if (rc == 1)
127 : {
128 0 : fprintf (stderr, "ERROR: Contract could not be ensured!\n%s\n", keyString (keyGetMeta (parentKey, "error/reason")));
129 0 : kdbClose (kdb, parentKey);
130 0 : keyDel (parentKey);
131 0 : removeSpec ();
132 0 : return EXIT_FAILURE;
133 : }
134 0 : else if (rc == -1)
135 : {
136 0 : fprintf (stderr, "ERROR: Contract malformed/NULL pointer!\n%s\n", keyString (keyGetMeta (parentKey, "error/reason")));
137 0 : kdbClose (kdb, parentKey);
138 0 : keyDel (parentKey);
139 0 : removeSpec ();
140 0 : return EXIT_FAILURE;
141 : }
142 :
143 0 : KeySet * ks = ksNew (0, KS_END);
144 0 : rc = kdbGet (kdb, ks, parentKey);
145 :
146 0 : if (rc == -1)
147 : {
148 0 : fprintf (stderr, "ERROR: kdbGet failed! %s\n", keyString (keyGetMeta (parentKey, "error/reason")));
149 0 : kdbClose (kdb, parentKey);
150 0 : keyDel (parentKey);
151 0 : ksDel (ks);
152 0 : removeSpec ();
153 0 : return EXIT_FAILURE;
154 : }
155 :
156 0 : Key * helpKey = ksLookupByName (ks, "proc/elektra/gopts/help", 0);
157 0 : if (helpKey != NULL && elektraStrCmp (keyString (helpKey), "1") == 0)
158 : {
159 0 : char * help = elektraGetOptsHelpMessage (helpKey, NULL, NULL);
160 0 : printf ("%s\n", help);
161 0 : elektraFree (help);
162 0 : kdbClose (kdb, parentKey);
163 0 : keyDel (parentKey);
164 0 : ksDel (ks);
165 0 : removeSpec ();
166 0 : return EXIT_SUCCESS;
167 : }
168 :
169 0 : printf ("When called with the same arguments 'rm' \n");
170 :
171 0 : Key * lookup = ksLookupByName (ks, BASE_KEY "/emptydirs", 0);
172 0 : if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
173 : {
174 0 : printf ("will delete empty directories\n");
175 : }
176 :
177 0 : lookup = ksLookupByName (ks, BASE_KEY "/force", 0);
178 0 : if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
179 : {
180 0 : printf ("will use force mode\n");
181 : }
182 :
183 0 : lookup = ksLookupByName (ks, BASE_KEY "/interactive", 0);
184 0 : if (lookup != NULL && elektraStrCmp (keyString (lookup), "never") == 0)
185 : {
186 0 : printf ("will not use interactive mode\n");
187 : }
188 0 : else if (lookup != NULL && elektraStrCmp (keyString (lookup), "once") == 0)
189 : {
190 0 : printf ("will use interactive mode; ask once\n");
191 : }
192 0 : else if (lookup != NULL && elektraStrCmp (keyString (lookup), "always") == 0)
193 : {
194 0 : printf ("will use interactive mode; always ask\n");
195 : }
196 :
197 0 : lookup = ksLookupByName (ks, BASE_KEY "/nopreserve", 0);
198 0 : if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
199 : {
200 0 : printf ("will not treat '/' specially\n");
201 : }
202 :
203 0 : lookup = ksLookupByName (ks, BASE_KEY "/preserve", 0);
204 0 : if (lookup != NULL && elektraStrCmp (keyString (lookup), "root") == 0)
205 : {
206 0 : printf ("will never remove '/'");
207 : }
208 0 : else if (lookup != NULL && elektraStrCmp (keyString (lookup), "all") == 0)
209 : {
210 0 : printf ("will reject any argument on separate device from its parent\n");
211 : }
212 :
213 0 : lookup = ksLookupByName (ks, BASE_KEY "/recursive", 0);
214 0 : if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
215 : {
216 0 : printf ("will delete recursively\n");
217 : }
218 :
219 0 : lookup = ksLookupByName (ks, BASE_KEY "/showversion", 0);
220 0 : if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
221 : {
222 0 : printf ("will show version and exit\n");
223 : }
224 :
225 0 : lookup = ksLookupByName (ks, BASE_KEY "/singlefs", 0);
226 0 : if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
227 : {
228 0 : printf ("will skip directories on different file systems\n");
229 : }
230 :
231 0 : lookup = ksLookupByName (ks, BASE_KEY "/verbose", 0);
232 0 : if (lookup != NULL && elektraStrCmp (keyString (lookup), "1") == 0)
233 : {
234 0 : printf ("will explain what is being done\n");
235 : }
236 :
237 0 : printf ("will remove the following files:\n");
238 :
239 0 : Key * arrayParent = ksLookupByName (ks, BASE_KEY "/files", 0);
240 0 : KeySet * files = elektraArrayGet (arrayParent, ks);
241 :
242 0 : ksRewind (files);
243 0 : Key * cur = NULL;
244 0 : while ((cur = ksNext (files)) != NULL)
245 : {
246 0 : printf (" %s\n", keyString (cur));
247 : }
248 0 : printf ("\n");
249 :
250 0 : kdbClose (kdb, parentKey);
251 0 : keyDel (parentKey);
252 0 : ksDel (ks);
253 :
254 0 : removeSpec ();
255 :
256 0 : return EXIT_SUCCESS;
257 : }
|