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