1 /*
2 ** Copyright (C) 2006-2020 by Carnegie Mellon University.
3 **
4 ** @OPENSOURCE_LICENSE_START@
5 ** See license information in ../../LICENSE.txt
6 ** @OPENSOURCE_LICENSE_END@
7 */
8
9 /*
10 ** Code to test the sklog module. Note that this C files #includes
11 ** the sklog.c source file.
12 **
13 */
14
15 /* set a testing flag */
16 #define SKLOG_TESTING_LOG 1
17
18 /* NOTE: pull in the sklog source file */
19 #include "sklog.c"
20
21 RCSIDENTVAR(rcsID_sklog_c, "$SiLK: sklog-test.c ef14e54179be 2020-04-14 21:57:45Z mthomas $");
22
23
24 /* LOCAL DEFINES AND TYPEDEFS */
25
26 /* where to write --help output */
27 #define USAGE_FH stdout
28
29
30 /* LOCAL VARIABLE DEFINITIONS */
31
32 /* the features to test; set by --test-features */
33 static int test_features = -1;
34
35 /* whether to print a message using EMERGMSG(); usually it is skipped */
36 static int test_emerg = 0;
37
38 /* whether to test threaded logging; default is no */
39 static int test_threaded = 0;
40
41 /* number of times to write a log message */
42 static uint32_t repeat_count = 5;
43
44 /* number of seconds to wait between writing to the log */
45 static uint32_t repeat_delay = 10;
46
47 /* number of times the logToAllLevels() function has been called */
48 static int global_count = 0;
49
50 /* mutex to protect the count */
51 static pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
52
53
54 /* OPTIONS SETUP */
55
56 typedef enum {
57 OPT_TEST_FEATURES,
58 OPT_TEST_EMERG,
59 OPT_TEST_THREADED,
60 OPT_REPEAT_COUNT,
61 OPT_REPEAT_DELAY
62 } appOptionsEnum;
63
64 static struct option appOptions[] = {
65 {"test-features", REQUIRED_ARG, 0, OPT_TEST_FEATURES},
66 {"test-emerg", NO_ARG, 0, OPT_TEST_EMERG},
67 {"test-threaded", NO_ARG, 0, OPT_TEST_THREADED},
68 {"repeat-count", REQUIRED_ARG, 0, OPT_REPEAT_COUNT},
69 {"repeat-delay", REQUIRED_ARG, 0, OPT_REPEAT_DELAY},
70 {0,0,0,0} /* sentinel entry */
71 };
72
73 static const char *appHelp[] = {
74 ("The features to test. Passed to sklogSetup(). Sum of:\n"
75 "\t1 Enable options for use of syslog\n"
76 "\t2 Enable options that mimic SiLK legacy logging"),
77 "Test EMERGMSG() as well (usually it is skipped)",
78 "Test threaded logging. Def. no",
79 "Number of times to write messages to the log. Def. 5",
80 "Number of seconds between writes to the log. Def. 10",
81 (char *)NULL
82 };
83
84
85 /* LOCAL FUNCTION PROTOTYPES */
86
87 static int appOptionsHandler(clientData cData, int opt_index, char *opt_arg);
88
89
90 /* FUNCTION DEFINITIONS */
91
92 /*
93 * appUsageLong();
94 *
95 * Print complete usage information to USAGE_FH. Pass this
96 * function to skOptionsSetUsageCallback(); skOptionsParse() will
97 * call this funciton and then exit the program when the --help
98 * option is given.
99 */
100 static void
appUsageLong(void)101 appUsageLong(
102 void)
103 {
104 #define USAGE_MSG \
105 ("--test-features=FEATURES [SWITCHES]\n" \
106 "\tSimple code to test the sklog module.\n" \
107 "\tUse ``--test-features=FEATURES --help'' to see the options\n" \
108 "\tthat sklog will provide for various feature levels.\n" \
109 "\tNOTE: Attempting to use a \"Log switch\" before specifying\n" \
110 "\t--test-features results in an \"unrecognized option\" error.\n")
111
112 FILE *fh = USAGE_FH;
113
114 skAppStandardUsage(fh, USAGE_MSG, appOptions, appHelp);
115
116 fprintf(fh, "\nLog switches:\n");
117 sklogOptionsUsage(fh);
118 }
119
120
121 /*
122 * appTeardown()
123 *
124 * Teardown all modules, close all files, and tidy up all
125 * application state.
126 *
127 * This function is idempotent.
128 */
129 static void
appTeardown(void)130 appTeardown(
131 void)
132 {
133 static int teardownFlag = 0;
134
135 if (teardownFlag) {
136 return;
137 }
138 teardownFlag = 1;
139
140 sklogTeardown();
141 skAppUnregister();
142 }
143
144
145 /*
146 * appSetup(argc, argv);
147 *
148 * Perform all the setup for this application include setting up
149 * required modules, parsing options, etc. This function should be
150 * passed the same arguments that were passed into main().
151 *
152 * Returns to the caller if all setup succeeds. If anything fails,
153 * this function will cause the application to exit with a FAILURE
154 * exit status.
155 */
156 static void
appSetup(int argc,char ** argv)157 appSetup(
158 int argc,
159 char **argv)
160 {
161 SILK_FEATURES_DEFINE_STRUCT(features);
162 int arg_index;
163
164 /* verify same number of options and help strings */
165 assert((sizeof(appHelp)/sizeof(char *)) ==
166 (sizeof(appOptions)/sizeof(struct option)));
167
168 /* register the application */
169 skAppRegister(argv[0]);
170 skAppVerifyFeatures(&features, NULL);
171 skOptionsSetUsageCallback(&appUsageLong);
172
173 /* register the options */
174 if (skOptionsRegister(appOptions, &appOptionsHandler, NULL))
175 {
176 skAppPrintErr("Unable to register options");
177 exit(EXIT_FAILURE);
178 }
179
180 /* parse the options */
181 arg_index = skOptionsParse(argc, argv);
182 if (arg_index < 0) {
183 /* options parsing should print error */
184 skAppUsage(); /* never returns */
185 }
186
187 /* check for extraneous arguments */
188 if (arg_index != argc) {
189 skAppPrintErr("Too many arguments or unrecognized switch '%s'",
190 argv[arg_index]);
191 skAppUsage(); /* never returns */
192 }
193
194 if (test_features < 0) {
195 skAppPrintErr("The --%s switch is required",
196 appOptions[OPT_TEST_FEATURES].name);
197 skAppUsage();
198 }
199
200 if (test_threaded) {
201 sklogEnableThreadedLogging();
202 }
203
204 /* verify logging */
205 if (sklogOptionsVerify()) {
206 exit(EXIT_FAILURE);
207 }
208
209 /* write command line */
210 sklogCommandLine(argc, argv);
211
212 /* register the teardown handler */
213 if (atexit(appTeardown) < 0) {
214 skAppPrintErr("Unable to register appTeardown() with atexit()");
215 appTeardown();
216 exit(EXIT_FAILURE);
217 }
218
219 return; /* OK */
220 }
221
222
223 /*
224 * status = appOptionsHandler(cData, opt_index, opt_arg);
225 *
226 * This function is passed to skOptionsRegister(); it will be called
227 * by skOptionsParse() for each user-specified switch that the
228 * application has registered; it should handle the switch as
229 * required---typically by setting global variables---and return 1
230 * if the switch processing failed or 0 if it succeeded. Returning
231 * a non-zero from from the handler causes skOptionsParse() to return
232 * a negative value.
233 *
234 * The clientData in 'cData' is typically ignored; 'opt_index' is
235 * the index number that was specified as the last value for each
236 * struct option in appOptions[]; 'opt_arg' is the user's argument
237 * to the switch for options that have a REQUIRED_ARG or an
238 * OPTIONAL_ARG.
239 */
240 static int
appOptionsHandler(clientData UNUSED (cData),int opt_index,char * opt_arg)241 appOptionsHandler(
242 clientData UNUSED(cData),
243 int opt_index,
244 char *opt_arg)
245 {
246 uint32_t val;
247 int rv;
248
249 switch ((appOptionsEnum)opt_index) {
250 case OPT_TEST_FEATURES:
251 rv = skStringParseUint32(&val, opt_arg, SKLOG_FEATURE_SYSLOG,
252 (SKLOG_FEATURE_LEGACY | SKLOG_FEATURE_SYSLOG));
253 if (rv) {
254 goto PARSE_ERROR;
255 }
256 test_features = (int)val;
257 if (sklogSetup(test_features)) {
258 skAppPrintErr("Unable to setup log");
259 exit(EXIT_FAILURE);
260 }
261 break;
262
263 case OPT_TEST_EMERG:
264 test_emerg = 1;
265 break;
266
267 case OPT_TEST_THREADED:
268 test_threaded = 1;
269 break;
270
271 case OPT_REPEAT_COUNT:
272 rv = skStringParseUint32(&val, opt_arg, 0, 0);
273 if (rv) {
274 goto PARSE_ERROR;
275 }
276 repeat_count = val;
277 break;
278
279 case OPT_REPEAT_DELAY:
280 rv = skStringParseUint32(&val, opt_arg, 0, 0);
281 if (rv) {
282 goto PARSE_ERROR;
283 }
284 repeat_delay = val;
285 break;
286 }
287
288 return 0; /* OK */
289
290 PARSE_ERROR:
291 skAppPrintErr("Invalid %s '%s': %s",
292 appOptions[opt_index].name, opt_arg,
293 skStringParseStrerror(rv));
294 return 1;
295 }
296
297
298 static void
logToAllLevels(void * v_name)299 logToAllLevels(
300 void *v_name)
301 {
302 #define ST_ND_TH(c) \
303 ((((c) % 10) == 1) ? "st" : ((((c) % 10) == 2) ? "nd" : "th"))
304
305 int c;
306 char *name = (char *)v_name;
307
308 pthread_mutex_lock(&count_mutex);
309 c = ++global_count;
310 pthread_mutex_unlock(&count_mutex);
311
312 if (test_emerg) {
313 EMERGMSG("Writing a EMERGMSG for the %d%s time [%s]",
314 c, ST_ND_TH(c), name);
315 }
316 ALERTMSG("Writing a ALERTMSG for the %d%s time [%s]",
317 c, ST_ND_TH(c), name);
318 CRITMSG("Writing a CRITMSG for the %d%s time [%s]",
319 c, ST_ND_TH(c), name);
320 ERRMSG("Writing a ERRMSG for the %d%s time [%s]",
321 c, ST_ND_TH(c), name);
322 WARNINGMSG("Writing a WARNINGMSG for the %d%s time [%s]",
323 c, ST_ND_TH(c), name);
324 NOTICEMSG("Writing a NOTICEMSG for the %d%s time [%s]",
325 c, ST_ND_TH(c), name);
326 INFOMSG("Writing a INFOMSG for the %d%s time [%s]",
327 c, ST_ND_TH(c), name);
328 DEBUGMSG("Writing a DEBUGMSG for the %d%s time [%s]",
329 c, ST_ND_TH(c), name);
330 }
331
332
333 /*
334 * THREAD ENTRY POINT
335 */
336 static void *
write_msg_thread(void * v_name)337 write_msg_thread(
338 void *v_name)
339 {
340 uint32_t i;
341
342 for (i = 0; i < repeat_count; ++i) {
343 if (i > 0) {
344 sleep(repeat_delay);
345 }
346 logToAllLevels(v_name);
347 }
348
349 return v_name;
350 }
351
352
main(int argc,char ** argv)353 int main(int argc, char **argv)
354 {
355 appSetup(argc, argv); /* never returns on error */
356
357 if (sklogOpen()) {
358 skAppPrintErr("Unable to open log");
359 }
360 INFOMSG("Current log level is %s and log mask is %d",
361 sklogGetLevel(), sklogGetMask());
362
363 if (!test_threaded) {
364 write_msg_thread((void*)"main");
365 } else {
366 pthread_t p1;
367 pthread_t p2;
368
369 pthread_create(&p1, NULL, &write_msg_thread, (void*)"p1");
370 pthread_create(&p2, NULL, &write_msg_thread, (void*)"p2");
371 logToAllLevels((void*)"main");
372 pthread_join(p2, NULL);
373 pthread_join(p1, NULL);
374 }
375
376 sklogClose();
377
378 return 0;
379 }
380
381
382 /*
383 ** Local Variables:
384 ** mode:c
385 ** indent-tabs-mode:nil
386 ** c-basic-offset:4
387 ** End:
388 */
389