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 "dump.hpp"
10 :
11 : using namespace ckdb;
12 :
13 : #include <fstream>
14 : #include <iostream>
15 : #include <sstream>
16 : #include <string>
17 : #include <vector>
18 :
19 : #include <unistd.h>
20 :
21 : #include <kdberrors.h>
22 : #include <kdblogger.h>
23 :
24 : namespace dump
25 : {
26 :
27 2472 : int serialise (std::ostream & os, ckdb::Key *, ckdb::KeySet * ks)
28 : {
29 : ckdb::Key * cur;
30 :
31 4944 : os << "kdbOpen 1" << std::endl;
32 :
33 9888 : os << "ksNew " << ckdb::ksGetSize (ks) << std::endl;
34 :
35 2472 : ckdb::KeySet * metacopies = ckdb::ksNew (0, KS_END);
36 :
37 2472 : ksRewind (ks);
38 146359 : while ((cur = ksNext (ks)) != nullptr)
39 : {
40 143887 : size_t namesize = ckdb::keyGetNameSize (cur);
41 143887 : size_t valuesize = ckdb::keyGetValueSize (cur);
42 719435 : os << "keyNew " << namesize << " " << valuesize << std::endl;
43 143887 : os.write (ckdb::keyName (cur), namesize);
44 143887 : os.write (static_cast<const char *> (ckdb::keyValue (cur)), valuesize);
45 143887 : os << std::endl;
46 :
47 : const ckdb::Key * meta;
48 143887 : ckdb::keyRewindMeta (cur);
49 427213 : while ((meta = ckdb::keyNextMeta (cur)) != nullptr)
50 : {
51 283326 : std::stringstream ss;
52 283326 : ss << "user/" << meta; // use the address of pointer as name
53 :
54 424989 : ckdb::Key * search = ckdb::keyNew (ss.str ().c_str (), KEY_END);
55 141663 : ckdb::Key * ret = ksLookup (metacopies, search, 0);
56 :
57 141663 : if (!ret)
58 : {
59 : /* This metakey was not serialised up to now */
60 141619 : size_t metanamesize = ckdb::keyGetNameSize (meta);
61 141619 : size_t metavaluesize = ckdb::keyGetValueSize (meta);
62 :
63 708095 : os << "keyMeta " << metanamesize << " " << metavaluesize << std::endl;
64 141619 : os.write (ckdb::keyName (meta), metanamesize);
65 141619 : os.write (static_cast<const char *> (ckdb::keyValue (meta)), metavaluesize);
66 141619 : os << std::endl;
67 :
68 283238 : std::stringstream ssv;
69 566476 : ssv << namesize << " " << metanamesize << std::endl;
70 141619 : ssv.write (ckdb::keyName (cur), namesize);
71 141619 : ssv.write (ckdb::keyName (meta), metanamesize);
72 708095 : ckdb::keySetRaw (search, ssv.str ().c_str (), ssv.str ().size ());
73 :
74 141619 : ksAppendKey (metacopies, search);
75 : }
76 : else
77 : {
78 : /* Meta key already serialised, write out a reference to it */
79 44 : keyDel (search);
80 :
81 44 : os << "keyCopyMeta ";
82 44 : os.write (static_cast<const char *> (ckdb::keyValue (ret)), ckdb::keyGetValueSize (ret));
83 : os << std::endl;
84 : }
85 : }
86 143887 : os << "keyEnd" << std::endl;
87 : }
88 4944 : os << "ksEnd" << std::endl;
89 :
90 2472 : ksDel (metacopies);
91 :
92 2472 : return 1;
93 : }
94 :
95 957 : int unserialise (std::istream & is, ckdb::Key * errorKey, ckdb::KeySet * ks)
96 : {
97 957 : ckdb::Key * cur = nullptr;
98 :
99 3828 : std::vector<char> namebuffer (4048);
100 3828 : std::vector<char> valuebuffer (4048);
101 1914 : std::string line;
102 957 : std::string command;
103 : size_t nrKeys;
104 : size_t namesize;
105 : size_t valuesize;
106 :
107 39344 : while (std::getline (is, line))
108 : {
109 38381 : std::stringstream ss (line);
110 19666 : ss >> command;
111 :
112 19666 : if (command == "kdbOpen")
113 : {
114 1902 : std::string version;
115 951 : ss >> version;
116 951 : if (version != "1")
117 : {
118 0 : ELEKTRA_SET_INSTALLATION_ERRORF (errorKey, "Wrong version detected in dumpfile: %s", version.c_str ());
119 0 : return -1;
120 : }
121 : }
122 18715 : else if (command == "ksNew")
123 : {
124 951 : ss >> nrKeys;
125 :
126 951 : ksClear (ks);
127 : }
128 17764 : else if (command == "keyNew")
129 : {
130 6406 : cur = ckdb::keyNew (nullptr);
131 :
132 6406 : ss >> namesize;
133 6406 : ss >> valuesize;
134 :
135 12812 : if (namesize > namebuffer.size ()) namebuffer.resize (namesize + 1);
136 6406 : is.read (&namebuffer[0], namesize);
137 12812 : namebuffer[namesize] = 0;
138 6406 : ckdb::keySetName (cur, &namebuffer[0]);
139 :
140 12812 : if (valuesize > valuebuffer.size ()) valuebuffer.resize (valuesize + 1);
141 6406 : is.read (&valuebuffer[0], valuesize);
142 12812 : valuebuffer[valuesize] = 0;
143 :
144 6406 : ckdb::keySetRaw (cur, &valuebuffer[0], valuesize);
145 6406 : std::getline (is, line);
146 : }
147 11358 : else if (command == "keyMeta")
148 : {
149 3957 : ss >> namesize;
150 3957 : ss >> valuesize;
151 :
152 7914 : if (namesize > namebuffer.size ()) namebuffer.resize (namesize + 1);
153 3957 : is.read (&namebuffer[0], namesize);
154 7914 : namebuffer[namesize] = 0;
155 :
156 7914 : if (valuesize > valuebuffer.size ()) valuebuffer.resize (valuesize + 1);
157 3957 : is.read (&valuebuffer[0], valuesize);
158 7914 : valuebuffer[valuesize] = 0;
159 :
160 3957 : keySetMeta (cur, &namebuffer[0], &valuebuffer[0]);
161 3957 : std::getline (is, line);
162 : }
163 7401 : else if (command == "keyCopyMeta")
164 : {
165 44 : ss >> namesize;
166 44 : ss >> valuesize;
167 :
168 88 : if (namesize > namebuffer.size ()) namebuffer.resize (namesize + 1);
169 44 : is.read (&namebuffer[0], namesize);
170 88 : namebuffer[namesize] = 0;
171 :
172 88 : if (valuesize > valuebuffer.size ()) valuebuffer.resize (valuesize + 1);
173 44 : is.read (&valuebuffer[0], valuesize);
174 88 : valuebuffer[valuesize] = 0;
175 :
176 44 : ckdb::Key * search = ckdb::ksLookupByName (ks, &namebuffer[0], 0);
177 44 : ckdb::keyCopyMeta (cur, search, &valuebuffer[0]);
178 44 : std::getline (is, line);
179 : }
180 7357 : else if (command == "keyEnd")
181 : {
182 6406 : ckdb::ksAppendKey (ks, cur);
183 : cur = nullptr;
184 : }
185 951 : else if (command == "ksEnd")
186 : {
187 : break;
188 : }
189 : else
190 : {
191 : // TODO: Solution
192 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
193 : errorKey,
194 : "Unknown command detected in dumpfile: %s.\nMaybe you use a different file format? "
195 : "Try to remount with another plugin (eg. ini, ni, etc.)",
196 : command.c_str ());
197 : return -1;
198 : }
199 : }
200 : return 1;
201 : }
202 :
203 : class pipebuf : public std::streambuf
204 : {
205 : char * buffer_;
206 : int fd_;
207 :
208 : public:
209 1152 : pipebuf (int fd) : buffer_ (new char[4096]), fd_ (fd)
210 : {
211 576 : }
212 576 : ~pipebuf ()
213 1728 : {
214 576 : delete[] this->buffer_;
215 576 : }
216 3659 : int underflow ()
217 : {
218 3659 : if (this->gptr () == this->egptr ())
219 : {
220 : // read from the pipe directly into the buffer
221 3659 : ssize_t r = read (fd_, buffer_, 4096);
222 3659 : this->setg (this->buffer_, this->buffer_, this->buffer_ + r);
223 : }
224 7314 : return this->gptr () == this->egptr () ? std::char_traits<char>::eof () :
225 7314 : std::char_traits<char>::to_int_type (*this->gptr ());
226 : }
227 : };
228 :
229 : } // namespace dump
230 :
231 : extern "C" {
232 :
233 3226 : int elektraDumpGet (ckdb::Plugin *, ckdb::KeySet * returned, ckdb::Key * parentKey)
234 : {
235 3226 : Key * root = ckdb::keyNew ("system/elektra/modules/dump", KEY_END);
236 3226 : if (keyRel (root, parentKey) >= 0)
237 : {
238 2269 : keyDel (root);
239 2269 : KeySet * n = ksNew (50, keyNew ("system/elektra/modules/dump", KEY_VALUE, "dump plugin waits for your orders", KEY_END),
240 : keyNew ("system/elektra/modules/dump/exports", KEY_END),
241 : keyNew ("system/elektra/modules/dump/exports/get", KEY_FUNC, elektraDumpGet, KEY_END),
242 : keyNew ("system/elektra/modules/dump/exports/set", KEY_FUNC, elektraDumpSet, KEY_END),
243 : keyNew ("system/elektra/modules/dump/exports/serialise", KEY_FUNC, dump::serialise, KEY_END),
244 : keyNew ("system/elektra/modules/dump/exports/unserialise", KEY_FUNC, dump::unserialise, KEY_END),
245 : keyNew ("system/elektra/modules/dump/config/needs/fcrypt/textmode", KEY_VALUE, "0", KEY_END),
246 : #include "readme_dump.c"
247 2269 : keyNew ("system/elektra/modules/dump/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
248 2269 : ksAppend (returned, n);
249 2269 : ksDel (n);
250 : return 1;
251 : }
252 957 : keyDel (root);
253 957 : int errnosave = errno;
254 :
255 : // We use dump for the pluginprocess library. Unfortunately on macOS reading from /dev/fd/<fd> via
256 : // ifstream fails, thus we read directly from unnamed pipes using a custom buffer and read
257 957 : const char pipe[] = "/dev/fd/";
258 957 : if (!strncmp (keyString (parentKey), pipe, strlen (pipe)))
259 : {
260 2304 : int fd = std::stoi (std::string (keyString (parentKey) + strlen (pipe)));
261 1152 : dump::pipebuf pipebuf (fd);
262 1152 : std::istream is (&pipebuf);
263 576 : return dump::unserialise (is, parentKey, returned);
264 : }
265 : else
266 : {
267 : // ELEKTRA_LOG (ELEKTRA_LOG_MODULE_DUMP, "opening file %s", keyString (parentKey));
268 762 : std::ifstream is (keyString (parentKey), std::ios::binary);
269 :
270 381 : if (!is.is_open ())
271 : {
272 0 : ELEKTRA_SET_ERROR_GET (parentKey);
273 0 : errno = errnosave;
274 0 : return -1;
275 : }
276 :
277 381 : return dump::unserialise (is, parentKey, returned);
278 : }
279 : }
280 :
281 2472 : int elektraDumpSet (ckdb::Plugin *, ckdb::KeySet * returned, ckdb::Key * parentKey)
282 : {
283 2472 : int errnosave = errno;
284 : // ELEKTRA_LOG (ELEKTRA_LOG_MODULE_DUMP, "opening file %s", keyString (parentKey));
285 4944 : std::ofstream ofs (keyString (parentKey), std::ios::binary);
286 2472 : if (!ofs.is_open ())
287 : {
288 0 : ELEKTRA_SET_ERROR_SET (parentKey);
289 0 : errno = errnosave;
290 0 : return -1;
291 : }
292 :
293 2472 : return dump::serialise (ofs, parentKey, returned);
294 : }
295 :
296 7484 : ckdb::Plugin * ELEKTRA_PLUGIN_EXPORT
297 : {
298 : // clang-format off
299 : return elektraPluginExport("dump",
300 : ELEKTRA_PLUGIN_GET, &elektraDumpGet,
301 : ELEKTRA_PLUGIN_SET, &elektraDumpSet,
302 7484 : ELEKTRA_PLUGIN_END);
303 : }
304 :
305 2792 : } // extern C
306 :
|