1 #include <stdlib.h>
2 #include <sys/stat.h>
3 #include <cf3.defs.h>
4 #include <known_dirs.h>
5 
6 #include <dbm_api.h>
7 
8 
9 #define MAX_THREADS 10000
10 #define DB_ID dbid_classes
11 
12 #define STATUS_SUCCESS 0
13 #define STATUS_FAILED_OPEN 1
14 #define STATUS_FAILED_CLOSE 2
15 #define STATUS_ERROR 3
16 
17 
18 #define READWRITEKEY 123123123
19 #define READWRITEDATA1 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
20 #define READWRITEDATA2 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
21 
22 #define RECORD_COUNT_JUNK 7000
23 #define RECORD_COUNT_READWRITE 1  // only one read/write key above
24 #define RECORD_COUNT_TOTAL (RECORD_COUNT_JUNK + RECORD_COUNT_READWRITE)
25 #define VALUE_OFFSET1 10000
26 #define VALUE_OFFSET2 100000
27 
28 char CFWORKDIR[CF_BUFSIZE];
29 
30 static void WriteReadWriteData(CF_DB *db);
31 static bool ReadWriteDataIsValid(char *data);
32 static void DBWriteTestData(CF_DB *db);
33 static void TestReadWriteData(CF_DB *db);
34 static void TestCursorIteration(CF_DB *db);
35 
tests_setup(void)36 static void tests_setup(void)
37 {
38     static char env[] = /* Needs to be static for putenv() */
39         "CFENGINE_TEST_OVERRIDE_WORKDIR=/tmp/db_load.XXXXXX";
40 
41     char *workdir = strchr(env, '=') + 1; /* start of the path */
42     assert(workdir - 1 && workdir[0] == '/');
43 
44     mkdtemp(workdir);
45     strlcpy(CFWORKDIR, workdir, CF_BUFSIZE);
46     putenv(env);
47     mkdir(GetStateDir(), (S_IRWXU | S_IRWXG | S_IRWXO));
48 }
49 
contend(ARG_UNUSED void * param)50 static void *contend(ARG_UNUSED void *param)
51 {
52     CF_DB *db;
53 
54     if (!OpenDB(&db, DB_ID))
55     {
56         return (void *)STATUS_FAILED_OPEN;
57     }
58 
59     DBWriteTestData(db);
60 
61     TestReadWriteData(db);
62     TestCursorIteration(db);
63 
64     CloseDB(db);
65 
66     return (void *)STATUS_SUCCESS;
67 }
68 
TestReadWriteData(CF_DB * db)69 static void TestReadWriteData(CF_DB *db)
70 {
71     WriteReadWriteData(db);
72 
73     int iterations = rand() % 1000000;
74 
75     for(int i = 0; i < iterations; i++)
76     {
77         // sleep gets complicated in threads...
78     }
79 
80     static const int key = READWRITEKEY;
81 
82     char readData[sizeof(READWRITEDATA1)];
83 
84     if(!ReadComplexKeyDB(db, (const char *)&key, sizeof(key), readData, sizeof(readData)))
85     {
86         printf("Error read\n");
87     }
88 
89     if(!ReadWriteDataIsValid(readData))
90     {
91         printf("corrupt data: \"%s\"\n", readData);
92     }
93 }
94 
CoinFlip(void)95 static bool CoinFlip(void)
96 {
97     return rand() % 2 == 0;
98 }
99 
WriteReadWriteData(CF_DB * db)100 static void WriteReadWriteData(CF_DB *db)
101 {
102     const char *const data = CoinFlip() ? READWRITEDATA1 : READWRITEDATA2;
103     static const int key = READWRITEKEY;
104 
105     if(!WriteComplexKeyDB(db, (const char *)&key, sizeof(key), data, sizeof(READWRITEDATA1)))
106     {
107         printf("Error write!\n");
108         pthread_exit((void*)STATUS_ERROR);
109     }
110 }
111 
ReadWriteDataIsValid(char * data)112 static bool ReadWriteDataIsValid(char *data)
113 {
114     return (strcmp(data, READWRITEDATA1) == 0 ||
115             strcmp(data, READWRITEDATA2) == 0);
116 }
117 
TestCursorIteration(CF_DB * db)118 static void TestCursorIteration(CF_DB *db)
119 {
120     CF_DBC *dbc;
121 
122     if(!NewDBCursor(db, &dbc))
123     {
124         fprintf(stderr, "Test: could not create cursor");
125         pthread_exit((void*)STATUS_ERROR);
126         exit(EXIT_FAILURE);
127     }
128 
129     char *key;
130     void *value;
131     int key_sz, value_sz;
132 
133     int count = 0;
134     while(NextDB(dbc, &key, &key_sz, &value, &value_sz))
135     {
136         int key_num = *(int *)key;
137         int value_num = *(int *)value;
138 
139         if(key_num >= 0 && key_num < RECORD_COUNT_JUNK)
140         {
141             if((key_num + VALUE_OFFSET1 != value_num) && (key_num + VALUE_OFFSET2 != value_num))
142             {
143                 printf("Error: key,value %d,%d are inconsistent\n", key_num, value_num);
144             }
145         }
146         else if(key_num == READWRITEKEY)
147         {
148             if(!ReadWriteDataIsValid(value))
149             {
150                 printf("Error: ReadWrite data is invalid\n");
151             }
152         }
153         else
154         {
155             printf("Error: invalid key \"%s\"", key);
156         }
157 
158         count++;
159     }
160 
161     if(count != RECORD_COUNT_TOTAL)
162     {
163         printf("Error: During iteration count was %d (expected %d)\n", count, RECORD_COUNT_TOTAL);
164     }
165 
166     if(!DeleteDBCursor(dbc))
167     {
168         fprintf(stderr, "Test: could not delete cursor");
169         exit(EXIT_FAILURE);
170     }
171 
172 }
173 
174 
WriteReturnValues(int retvals[MAX_THREADS],pthread_t tids[MAX_THREADS],int numthreads)175 int WriteReturnValues(int retvals[MAX_THREADS], pthread_t tids[MAX_THREADS], int numthreads)
176 {
177     int failures = 0;
178 
179     for(int i = 0; i < numthreads; i++)
180     {
181         uintptr_t status;
182         pthread_join(tids[i], (void **)&status);
183         retvals[i] = status;
184 
185         if(status != STATUS_SUCCESS)
186         {
187             failures++;
188         }
189     }
190 
191     return failures;
192 }
193 
Cleanup(void)194 static void Cleanup(void)
195 {
196     char cmd[CF_BUFSIZE];
197     xsnprintf(cmd, CF_BUFSIZE, "rm -rf '%s'", CFWORKDIR);
198     system(cmd);
199 }
200 
main(int argc,char ** argv)201 int main(int argc, char **argv)
202 {
203     if (argc != 2)
204     {
205         fprintf(stderr, "Usage: db_load <num_threads>\n");
206         exit(EXIT_FAILURE);
207     }
208 
209     /* To clean up after databases are closed */
210     atexit(&Cleanup);
211 
212     tests_setup();
213 
214     int numthreads = atoi(argv[1]);
215 
216     assert(numthreads < MAX_THREADS);
217 
218     srand(time(NULL));
219 
220     pthread_t tids[MAX_THREADS];
221     pthread_attr_t attr;
222 
223     pthread_attr_init(&attr);
224     pthread_attr_setstacksize(&attr, 65536);
225     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
226 
227     for (int i = 0; i < numthreads; ++i)
228     {
229         int ret = pthread_create(&(tids[i]), &attr, &contend, NULL);
230 
231         if (ret != 0)
232         {
233             fprintf(stderr, "Unable to create thread: %s\n", strerror(ret));
234         }
235     }
236 
237     pthread_attr_destroy(&attr);
238 
239     int retvals[MAX_THREADS];
240 
241     int failures = WriteReturnValues(retvals, tids, numthreads);
242 
243     exit(failures);
244 }
245 
246 
DBWriteTestData(CF_DB * db)247 static void DBWriteTestData(CF_DB *db)
248 {
249     for(int i = 0; i < RECORD_COUNT_JUNK; i++)
250     {
251         bool flip = CoinFlip();
252         int value_num = i + (flip ? VALUE_OFFSET1 : VALUE_OFFSET2);
253 
254         if (!WriteComplexKeyDB(db, (const char *)&i, sizeof(i), &value_num, sizeof(value_num)))
255         {
256             Log(LOG_LEVEL_ERR, "Unable to write data to database");
257             pthread_exit((void*)STATUS_ERROR);
258         }
259     }
260 
261     WriteReadWriteData(db);
262 }
263 
264 /* Stub out */
265 
FatalError(ARG_UNUSED const EvalContext * ctx,char * fmt,...)266 void FatalError(ARG_UNUSED const EvalContext *ctx, char *fmt, ...)
267 {
268     if (fmt)
269     {
270         va_list ap;
271         char buf[CF_BUFSIZE] = "";
272 
273         va_start(ap, fmt);
274         vsnprintf(buf, CF_BUFSIZE - 1, fmt, ap);
275         va_end(ap);
276         Log(LOG_LEVEL_ERR, "Fatal CFEngine error: %s", buf);
277     }
278     else
279     {
280         Log(LOG_LEVEL_ERR, "Fatal CFEngine error (no description)");
281     }
282 
283     exit(EXIT_FAILURE);
284 }
285 
286 
287 pthread_mutex_t test_lock = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
288 
289 pthread_mutex_t *cft_dbhandle;
290 
291 const char *const DAY_TEXT[] = {};
292 const char *const MONTH_TEXT[] = {};
293