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