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