1 #include <stdlib.h>
2 #include <sys/stat.h>
3 #include <test.h>
4 #include <known_dirs.h>
5
6 #include <cf3.defs.h>
7 #include <dbm_api.h>
8 #include <file_lib.h>
9 #include <files_copy.h>
10 #include <misc_lib.h> /* xsnprintf */
11
12
13 char CFWORKDIR[CF_BUFSIZE];
14
tests_setup(void)15 void tests_setup(void)
16 {
17 static char env[] = /* Needs to be static for putenv() */
18 "CFENGINE_TEST_OVERRIDE_WORKDIR=/tmp/db_test.XXXXXX";
19
20 char *workdir = strchr(env, '=') + 1; /* start of the path */
21 assert(workdir - 1 && workdir[0] == '/');
22
23 mkdtemp(workdir);
24 strlcpy(CFWORKDIR, workdir, CF_BUFSIZE);
25 putenv(env);
26 mkdir(GetStateDir(), (S_IRWXU | S_IRWXG | S_IRWXO));
27 }
28
tests_teardown(void)29 void tests_teardown(void)
30 {
31 char cmd[CF_BUFSIZE];
32 xsnprintf(cmd, CF_BUFSIZE, "rm -rf '%s'", CFWORKDIR);
33 system(cmd);
34 }
35
test_open_close(void)36 void test_open_close(void)
37 {
38 // Test that we can simply open and close a database without doing anything.
39 CF_DB *db;
40 assert_int_equal(OpenDB(&db, dbid_classes), true);
41 CloseDB(db);
42 }
43
test_read_write(void)44 void test_read_write(void)
45 {
46 // Test that we can do normal reads and write, and that the values are
47 // reflected in the database.
48 CF_DB *db;
49 char value[CF_BUFSIZE];
50 strcpy(value, "myvalue");
51 int vsize = strlen(value) + 1;
52
53 assert_int_equal(OpenDB(&db, dbid_classes), true);
54
55 assert_int_equal(ReadDB(db, "written_entry", &value, vsize), false);
56 assert_string_equal(value, "myvalue");
57 assert_int_equal(WriteDB(db, "written_entry", value, vsize), true);
58 strcpy(value, "");
59 assert_int_equal(ReadDB(db, "written_entry", &value, vsize), true);
60 assert_string_equal(value, "myvalue");
61
62 CloseDB(db);
63
64 // Check also after we reopen the database.
65 assert_int_equal(OpenDB(&db, dbid_classes), true);
66 strcpy(value, "");
67 assert_int_equal(ReadDB(db, "written_entry", &value, vsize), true);
68 assert_string_equal(value, "myvalue");
69
70 CloseDB(db);
71 }
72
test_iter_modify_entry(void)73 void test_iter_modify_entry(void)
74 {
75 /* Test that deleting entry under cursor does not interrupt iteration */
76
77 CF_DB *db;
78 assert_int_equal(OpenDB(&db, dbid_classes), true);
79
80 assert_int_equal(WriteDB(db, "foobar", "abc", 3), true);
81 assert_int_equal(WriteDB(db, "bazbaz", "def", 3), true);
82 assert_int_equal(WriteDB(db, "booo", "ghi", 3), true);
83
84 CF_DBC *cursor;
85 assert_int_equal(NewDBCursor(db, &cursor), true);
86
87 char *key;
88 int ksize;
89 void *value;
90 int vsize;
91
92 assert_int_equal(NextDB(cursor, &key, &ksize, &value, &vsize), true);
93
94 assert_int_equal(DBCursorWriteEntry(cursor, "eee", 3), true);
95
96 assert_int_equal(NextDB(cursor, &key, &ksize, &value, &vsize), true);
97 assert_int_equal(NextDB(cursor, &key, &ksize, &value, &vsize), true);
98
99 assert_int_equal(DeleteDBCursor(cursor), true);
100
101 CloseDB(db);
102 }
103
104
test_iter_delete_entry(void)105 void test_iter_delete_entry(void)
106 {
107 /* Test that deleting entry under cursor does not interrupt iteration */
108
109 CF_DB *db;
110 assert_int_equal(OpenDB(&db, dbid_classes), true);
111
112 assert_int_equal(WriteDB(db, "foobar", "abc", 3), true);
113 assert_int_equal(WriteDB(db, "bazbaz", "def", 3), true);
114 assert_int_equal(WriteDB(db, "booo", "ghi", 3), true);
115
116 CF_DBC *cursor;
117 assert_int_equal(NewDBCursor(db, &cursor), true);
118
119 char *key;
120 int ksize;
121 void *value;
122 int vsize;
123
124 assert_int_equal(NextDB(cursor, &key, &ksize, &value, &vsize), true);
125
126 assert_int_equal(DBCursorDeleteEntry(cursor), true);
127
128 assert_int_equal(NextDB(cursor, &key, &ksize, &value, &vsize), true);
129 assert_int_equal(NextDB(cursor, &key, &ksize, &value, &vsize), true);
130
131 assert_int_equal(DeleteDBCursor(cursor), true);
132
133 CloseDB(db);
134 }
135
136 #if defined(HAVE_LIBTOKYOCABINET) || defined(HAVE_LIBQDBM) || defined(HAVE_LIBLMDB)
CreateGarbage(const char * filename)137 static void CreateGarbage(const char *filename)
138 {
139 FILE *fh = fopen(filename, "w");
140 for(int i = 0; i < 2; ++i)
141 {
142 fwrite("some garbage!", 14, 1, fh);
143 }
144 fclose(fh);
145 }
146 #endif /* HAVE_LIBTOKYOCABINET || HAVE_LIBQDBM */
147
test_recreate(void)148 void test_recreate(void)
149 {
150 /* Test that recreating database works properly */
151 #ifdef HAVE_LIBTOKYOCABINET
152 char tcdb_db[CF_BUFSIZE];
153 xsnprintf(tcdb_db, CF_BUFSIZE, "%s/cf_classes.tcdb", GetStateDir());
154 CreateGarbage(tcdb_db);
155 #endif
156 #ifdef HAVE_LIBQDBM
157 char qdbm_db[CF_BUFSIZE];
158 xsnprintf(qdbm_db, CF_BUFSIZE, "%s/cf_classes.qdbm", GetStateDir());
159 CreateGarbage(qdbm_db);
160 #endif
161 #ifdef HAVE_LIBLMDB
162 char lmdb_db[CF_BUFSIZE];
163 xsnprintf(lmdb_db, CF_BUFSIZE, "%s/cf_classes.lmdb", CFWORKDIR);
164 CreateGarbage(lmdb_db);
165 #endif
166
167 CF_DB *db;
168 assert_int_equal(OpenDB(&db, dbid_classes), true);
169 CloseDB(db);
170 }
171
test_old_workdir_db_location(void)172 void test_old_workdir_db_location(void)
173 {
174 #ifndef LMDB
175 // We manipulate the LMDB file name directly. Not adapted to the others.
176 return;
177 #endif
178
179 CF_DB *db;
180
181 char *state_dir;
182
183 xasprintf(&state_dir, "%s%cstate", GetWorkDir(), FILE_SEPARATOR);
184
185 if (strcmp(GetStateDir(), state_dir) != 0)
186 {
187 // Test only works when statedir is $(workdir)/state.
188 free(state_dir);
189 return;
190 }
191
192 assert_true(OpenDB(&db, dbid_lastseen));
193 assert_true(WriteDB(db, "key", "first_value", strlen("first_value") + 1));
194 CloseDB(db);
195
196 char *old_db, *orig_db, *new_db;
197 // Due to caching of the path we need to use a different db when opening the
198 // second time, otherwise the path is not rechecked.
199 xasprintf(&orig_db, "%s%ccf_lastseen.lmdb", GetStateDir(), FILE_SEPARATOR);
200 xasprintf(&old_db, "%s%ccf_audit.lmdb", GetWorkDir(), FILE_SEPARATOR);
201 xasprintf(&new_db, "%s%ccf_audit.lmdb", GetStateDir(), FILE_SEPARATOR);
202
203 // Copy database to old location.
204 assert_true(CopyRegularFileDisk(orig_db, old_db));
205
206 // Change content.
207 assert_true(OpenDB(&db, dbid_lastseen));
208 assert_true(WriteDB(db, "key", "second_value", strlen("second_value") + 1));
209 CloseDB(db);
210
211 // Copy database to new location.
212 assert_true(CopyRegularFileDisk(orig_db, new_db));
213
214 char value[CF_BUFSIZE];
215
216 // Old location should take precedence.
217 assert_true(OpenDB(&db, dbid_audit));
218 assert_true(ReadDB(db, "key", value, sizeof(value)));
219 assert_string_equal(value, "first_value");
220 CloseDB(db);
221
222 free(state_dir);
223 free(old_db);
224 free(orig_db);
225 free(new_db);
226 }
227
main()228 int main()
229 {
230 PRINT_TEST_BANNER();
231 tests_setup();
232
233 const UnitTest tests[] =
234 {
235 unit_test(test_open_close),
236 unit_test(test_read_write),
237 unit_test(test_iter_modify_entry),
238 unit_test(test_iter_delete_entry),
239 unit_test(test_recreate),
240 unit_test(test_old_workdir_db_location),
241 };
242
243 PRINT_TEST_BANNER();
244 int ret = run_tests(tests);
245
246 tests_teardown();
247 return ret;
248 }
249
250 /* STUBS */
251
FatalError(ARG_UNUSED char * s,...)252 void FatalError(ARG_UNUSED char *s, ...)
253 {
254 fail();
255 exit(42);
256 }
257
258
259