1 /*
2  * This file is part of the zlog Library.
3  *
4  * Copyright (C) 2011 by Hardy Simpson <HardySimpson1984@gmail.com>
5  *
6  * Licensed under the LGPL v2.1, see the file COPYING in base directory.
7  */
8 
9 #include "fmacros.h"
10 
11 #include <sys/types.h>
12 #include <string.h>
13 #include <ctype.h>
14 #include <syslog.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <pthread.h>
21 
22 #include "rule.h"
23 #include "format.h"
24 #include "buf.h"
25 #include "thread.h"
26 #include "level_list.h"
27 #include "rotater.h"
28 #include "spec.h"
29 #include "conf.h"
30 
31 #include "zc_defs.h"
32 
33 
zlog_rule_profile(zlog_rule_t * a_rule,int flag)34 void zlog_rule_profile(zlog_rule_t * a_rule, int flag)
35 {
36 	int i;
37 	zlog_spec_t *a_spec;
38 
39 	zc_assert(a_rule,);
40 	zc_profile(flag, "---rule:[%p][%s%c%d]-[%d,%d][%s,%p,%d:%ld*%d~%s][%d][%d][%s:%s:%p];[%p]---",
41 		a_rule,
42 
43 		a_rule->category,
44 		a_rule->compare_char,
45 		a_rule->level,
46 
47 		a_rule->file_perms,
48 		a_rule->file_open_flags,
49 
50 		a_rule->file_path,
51 		a_rule->dynamic_specs,
52 		a_rule->static_fd,
53 
54 		a_rule->archive_max_size,
55 		a_rule->archive_max_count,
56 		a_rule->archive_path,
57 
58 		a_rule->pipe_fd,
59 
60 		a_rule->syslog_facility,
61 
62 		a_rule->record_name,
63 		a_rule->record_path,
64 		a_rule->record_func,
65 		a_rule->format);
66 
67 	if (a_rule->dynamic_specs) {
68 		zc_arraylist_foreach(a_rule->dynamic_specs, i, a_spec) {
69 			zlog_spec_profile(a_spec, flag);
70 		}
71 	}
72 	return;
73 }
74 
75 /*******************************************************************************/
76 
zlog_rule_output_static_file_single(zlog_rule_t * a_rule,zlog_thread_t * a_thread)77 static int zlog_rule_output_static_file_single(zlog_rule_t * a_rule, zlog_thread_t * a_thread)
78 {
79 	struct stat stb;
80 	int do_file_reload = 0;
81 	int redo_inode_stat = 0;
82 
83 	if (zlog_format_gen_msg(a_rule->format, a_thread)) {
84 		zc_error("zlog_format_gen_msg fail");
85 		return -1;
86 	}
87 
88 	/* check if the output file was changed by an external tool by comparing the inode to our saved off one */
89 	if (stat(a_rule->file_path, &stb)) {
90 		if (errno != ENOENT) {
91 			zc_error("stat fail on [%s], errno[%d]", a_rule->file_path, errno);
92 			return -1;
93 		} else {
94 			do_file_reload = 1;
95 			redo_inode_stat = 1; /* we'll have to restat the newly created file to get the inode info */
96 		}
97 	} else {
98 		do_file_reload = (stb.st_ino != a_rule->static_ino || stb.st_dev != a_rule->static_dev);
99 	}
100 
101 	if (do_file_reload) {
102 		close(a_rule->static_fd);
103 		a_rule->static_fd = open(a_rule->file_path,
104 			O_WRONLY | O_APPEND | O_CREAT | a_rule->file_open_flags,
105 			a_rule->file_perms);
106 		if (a_rule->static_fd < 0) {
107 			zc_error("open file[%s] fail, errno[%d]", a_rule->file_path, errno);
108 			return -1;
109 		}
110 
111 		/* save off the new dev/inode info from the stat call we already did */
112 		if (redo_inode_stat) {
113 			if (stat(a_rule->file_path, &stb)) {
114 				zc_error("stat fail on new file[%s], errno[%d]", a_rule->file_path, errno);
115 				return -1;
116 			}
117 		}
118 		a_rule->static_dev = stb.st_dev;
119 		a_rule->static_ino = stb.st_ino;
120 	}
121 
122 	if (write(a_rule->static_fd,
123 			zlog_buf_str(a_thread->msg_buf),
124 			zlog_buf_len(a_thread->msg_buf)) < 0) {
125 		zc_error("write fail, errno[%d]", errno);
126 		return -1;
127 	}
128 
129 	/* not so thread safe here, as multiple thread may ++fsync_count at the same time */
130 	if (a_rule->fsync_period && ++a_rule->fsync_count >= a_rule->fsync_period) {
131 		a_rule->fsync_count = 0;
132 		if (fsync(a_rule->static_fd)) {
133 			zc_error("fsync[%d] fail, errno[%d]", a_rule->static_fd, errno);
134 		}
135 	}
136 
137 	return 0;
138 }
139 
zlog_rule_gen_archive_path(zlog_rule_t * a_rule,zlog_thread_t * a_thread)140 static char * zlog_rule_gen_archive_path(zlog_rule_t *a_rule, zlog_thread_t *a_thread)
141 {
142 	int i;
143 	zlog_spec_t *a_spec;
144 
145 	if (!a_rule->archive_specs) return a_rule->archive_path;
146 
147 	zlog_buf_restart(a_thread->archive_path_buf);
148 
149 	zc_arraylist_foreach(a_rule->archive_specs, i, a_spec) {
150 		if (zlog_spec_gen_archive_path(a_spec, a_thread)) {
151 			zc_error("zlog_spec_gen_path fail");
152 			return NULL;
153 		}
154 	}
155 
156 	zlog_buf_seal(a_thread->archive_path_buf);
157 	return zlog_buf_str(a_thread->archive_path_buf);
158 }
159 
zlog_rule_output_static_file_rotate(zlog_rule_t * a_rule,zlog_thread_t * a_thread)160 static int zlog_rule_output_static_file_rotate(zlog_rule_t * a_rule, zlog_thread_t * a_thread)
161 {
162 	size_t len;
163 	struct zlog_stat info;
164 	int fd;
165 
166 	if (zlog_format_gen_msg(a_rule->format, a_thread)) {
167 		zc_error("zlog_format_gen_msg fail");
168 		return -1;
169 	}
170 
171 	fd = open(a_rule->file_path,
172 		a_rule->file_open_flags | O_WRONLY | O_APPEND | O_CREAT, a_rule->file_perms);
173 	if (fd < 0) {
174 		zc_error("open file[%s] fail, errno[%d]", a_rule->file_path, errno);
175 		return -1;
176 	}
177 
178 	len = zlog_buf_len(a_thread->msg_buf);
179 	if (write(fd, zlog_buf_str(a_thread->msg_buf), len) < 0) {
180 		zc_error("write fail, errno[%d]", errno);
181 		close(fd);
182 		return -1;
183 	}
184 
185 	if (a_rule->fsync_period && ++a_rule->fsync_count >= a_rule->fsync_period) {
186 		a_rule->fsync_count = 0;
187 		if (fsync(fd)) zc_error("fsync[%d] fail, errno[%d]", fd, errno);
188 	}
189 
190 	if (close(fd) < 0) {
191 		zc_error("close fail, maybe cause by write, errno[%d]", errno);
192 		return -1;
193 	}
194 
195 	if (a_rule->fsync_period && ++a_rule->fsync_count >= a_rule->fsync_period) {
196 		a_rule->fsync_count = 0;
197 		if (fsync(a_rule->static_fd)) {
198 			zc_error("fsync[%d] fail, errno[%d]", a_rule->static_fd, errno);
199 		}
200 	}
201 
202 	if (len > a_rule->archive_max_size) {
203 		zc_debug("one msg's len[%ld] > archive_max_size[%ld], no rotate",
204 			 (long)len, (long)a_rule->archive_max_size);
205 		return 0;
206 	}
207 
208 	if (stat(a_rule->file_path, &info)) {
209 		zc_warn("stat [%s] fail, errno[%d], maybe in rotating", a_rule->file_path, errno);
210 		return 0;
211 	}
212 
213 	/* file not so big, return */
214 	if (info.st_size + len < a_rule->archive_max_size) return 0;
215 
216 	if (zlog_rotater_rotate(zlog_env_conf->rotater,
217 		a_rule->file_path, len,
218 		zlog_rule_gen_archive_path(a_rule, a_thread),
219 		a_rule->archive_max_size, a_rule->archive_max_count)
220 		) {
221 		zc_error("zlog_rotater_rotate fail");
222 		return -1;
223 	} /* success or no rotation do nothing */
224 
225 	return 0;
226 }
227 
228 /* return path	success
229  * return NULL	fail
230  */
231 #define zlog_rule_gen_path(a_rule, a_thread) do {    \
232 	int i;    \
233 	zlog_spec_t *a_spec;    \
234     \
235 	zlog_buf_restart(a_thread->path_buf);    \
236     \
237 	zc_arraylist_foreach(a_rule->dynamic_specs, i, a_spec) {    \
238 		if (zlog_spec_gen_path(a_spec, a_thread)) {    \
239 			zc_error("zlog_spec_gen_path fail");    \
240 			return -1;    \
241 		}    \
242 	}    \
243     \
244 	zlog_buf_seal(a_thread->path_buf);    \
245 } while(0)
246 
247 
zlog_rule_output_dynamic_file_single(zlog_rule_t * a_rule,zlog_thread_t * a_thread)248 static int zlog_rule_output_dynamic_file_single(zlog_rule_t * a_rule, zlog_thread_t * a_thread)
249 {
250 	int fd;
251 
252 	zlog_rule_gen_path(a_rule, a_thread);
253 
254 	if (zlog_format_gen_msg(a_rule->format, a_thread)) {
255 		zc_error("zlog_format_output fail");
256 		return -1;
257 	}
258 
259 	fd = open(zlog_buf_str(a_thread->path_buf),
260 		a_rule->file_open_flags | O_WRONLY | O_APPEND | O_CREAT, a_rule->file_perms);
261 	if (fd < 0) {
262 		zc_error("open file[%s] fail, errno[%d]", zlog_buf_str(a_thread->path_buf), errno);
263 		return -1;
264 	}
265 
266 	if (write(fd, zlog_buf_str(a_thread->msg_buf), zlog_buf_len(a_thread->msg_buf)) < 0) {
267 		zc_error("write fail, errno[%d]", errno);
268 		close(fd);
269 		return -1;
270 	}
271 
272 	if (a_rule->fsync_period && ++a_rule->fsync_count >= a_rule->fsync_period) {
273 		a_rule->fsync_count = 0;
274 		if (fsync(fd)) zc_error("fsync[%d] fail, errno[%d]", fd, errno);
275 	}
276 
277 	if (close(fd) < 0) {
278 		zc_error("close fail, maybe cause by write, errno[%d]", errno);
279 		return -1;
280 	}
281 
282 	return 0;
283 }
284 
zlog_rule_output_dynamic_file_rotate(zlog_rule_t * a_rule,zlog_thread_t * a_thread)285 static int zlog_rule_output_dynamic_file_rotate(zlog_rule_t * a_rule, zlog_thread_t * a_thread)
286 {
287 	int fd;
288 	char *path;
289 	size_t len;
290 	struct zlog_stat info;
291 
292 	zlog_rule_gen_path(a_rule, a_thread);
293 
294 	if (zlog_format_gen_msg(a_rule->format, a_thread)) {
295 		zc_error("zlog_format_output fail");
296 		return -1;
297 	}
298 
299 	path = zlog_buf_str(a_thread->path_buf);
300 	fd = open(path, a_rule->file_open_flags | O_WRONLY | O_APPEND | O_CREAT, a_rule->file_perms);
301 	if (fd < 0) {
302 		zc_error("open file[%s] fail, errno[%d]", zlog_buf_str(a_thread->path_buf), errno);
303 		return -1;
304 	}
305 
306 	len = zlog_buf_len(a_thread->msg_buf);
307 	if (write(fd, zlog_buf_str(a_thread->msg_buf), len) < 0) {
308 		zc_error("write fail, errno[%d]", errno);
309 		close(fd);
310 		return -1;
311 	}
312 
313 	if (a_rule->fsync_period && ++a_rule->fsync_count >= a_rule->fsync_period) {
314 		a_rule->fsync_count = 0;
315 		if (fsync(fd)) zc_error("fsync[%d] fail, errno[%d]", fd, errno);
316 	}
317 
318 	if (close(fd) < 0) {
319 		zc_error("write fail, maybe cause by write, errno[%d]", errno);
320 		return -1;
321 	}
322 
323 	if (len > a_rule->archive_max_size) {
324 		zc_debug("one msg's len[%ld] > archive_max_size[%ld], no rotate",
325 			 (long)len, (long) a_rule->archive_max_size);
326 		return 0;
327 	}
328 
329 	if (stat(path, &info)) {
330 		zc_warn("stat [%s] fail, errno[%d], maybe in rotating", path, errno);
331 		return 0;
332 	}
333 
334 	/* file not so big, return */
335 	if (info.st_size + len < a_rule->archive_max_size) return 0;
336 
337 	if (zlog_rotater_rotate(zlog_env_conf->rotater,
338 		path, len,
339 		zlog_rule_gen_archive_path(a_rule, a_thread),
340 		a_rule->archive_max_size, a_rule->archive_max_count)
341 		) {
342 		zc_error("zlog_rotater_rotate fail");
343 		return -1;
344 	} /* success or no rotation do nothing */
345 
346 	return 0;
347 }
348 
zlog_rule_output_pipe(zlog_rule_t * a_rule,zlog_thread_t * a_thread)349 static int zlog_rule_output_pipe(zlog_rule_t * a_rule, zlog_thread_t * a_thread)
350 {
351 	if (zlog_format_gen_msg(a_rule->format, a_thread)) {
352 		zc_error("zlog_format_gen_msg fail");
353 		return -1;
354 	}
355 
356 	if (write(a_rule->pipe_fd,
357 			zlog_buf_str(a_thread->msg_buf),
358 			zlog_buf_len(a_thread->msg_buf)) < 0) {
359 		zc_error("write fail, errno[%d]", errno);
360 		return -1;
361 	}
362 
363 	return 0;
364 }
365 
zlog_rule_output_syslog(zlog_rule_t * a_rule,zlog_thread_t * a_thread)366 static int zlog_rule_output_syslog(zlog_rule_t * a_rule, zlog_thread_t * a_thread)
367 {
368 	zlog_level_t *a_level;
369 
370 	if (zlog_format_gen_msg(a_rule->format, a_thread)) {
371 		zc_error("zlog_format_gen_msg fail");
372 		return -1;
373 	}
374 
375 	/*
376 	msg = a_thread->msg_buf->start;
377 	msg_len = a_thread->msg_buf->end - a_thread->msg_buf->start;
378 	 */
379 
380 	a_level = zlog_level_list_get(zlog_env_conf->levels, a_thread->event->level);
381 	zlog_buf_seal(a_thread->msg_buf);
382 	syslog(a_rule->syslog_facility | a_level->syslog_level,
383 		"%s",  zlog_buf_str(a_thread->msg_buf));
384 	return 0;
385 }
386 
zlog_rule_output_static_record(zlog_rule_t * a_rule,zlog_thread_t * a_thread)387 static int zlog_rule_output_static_record(zlog_rule_t * a_rule, zlog_thread_t * a_thread)
388 {
389 	zlog_msg_t msg;
390 
391 	if (!a_rule->record_func) {
392 		zc_error("user defined record funcion for [%s] not set, no output",
393 			a_rule->record_name);
394 		return -1;
395 	}
396 
397 	if (zlog_format_gen_msg(a_rule->format, a_thread)) {
398 		zc_error("zlog_format_gen_msg fail");
399 		return -1;
400 	}
401 	zlog_buf_seal(a_thread->msg_buf);
402 
403 	msg.buf = zlog_buf_str(a_thread->msg_buf);
404 	msg.len = zlog_buf_len(a_thread->msg_buf);
405 	msg.path = a_rule->record_path;
406 
407 	if (a_rule->record_func(&msg)) {
408 		zc_error("a_rule->record fail");
409 		return -1;
410 	}
411 	return 0;
412 }
413 
zlog_rule_output_dynamic_record(zlog_rule_t * a_rule,zlog_thread_t * a_thread)414 static int zlog_rule_output_dynamic_record(zlog_rule_t * a_rule, zlog_thread_t * a_thread)
415 {
416 	zlog_msg_t msg;
417 
418 	if (!a_rule->record_func) {
419 		zc_error("user defined record funcion for [%s] not set, no output",
420 			a_rule->record_name);
421 		return -1;
422 	}
423 
424 	zlog_rule_gen_path(a_rule, a_thread);
425 
426 	if (zlog_format_gen_msg(a_rule->format, a_thread)) {
427 		zc_error("zlog_format_gen_msg fail");
428 		return -1;
429 	}
430 	zlog_buf_seal(a_thread->msg_buf);
431 
432 	msg.buf = zlog_buf_str(a_thread->msg_buf);
433 	msg.len = zlog_buf_len(a_thread->msg_buf);
434 	msg.path = zlog_buf_str(a_thread->path_buf);
435 
436 	if (a_rule->record_func(&msg)) {
437 		zc_error("a_rule->record fail");
438 		return -1;
439 	}
440 	return 0;
441 }
442 
zlog_rule_output_stdout(zlog_rule_t * a_rule,zlog_thread_t * a_thread)443 static int zlog_rule_output_stdout(zlog_rule_t * a_rule,
444 				   zlog_thread_t * a_thread)
445 {
446 
447 	if (zlog_format_gen_msg(a_rule->format, a_thread)) {
448 		zc_error("zlog_format_gen_msg fail");
449 		return -1;
450 	}
451 
452 	if (write(STDOUT_FILENO,
453 		zlog_buf_str(a_thread->msg_buf), zlog_buf_len(a_thread->msg_buf)) < 0) {
454 		zc_error("write fail, errno[%d]", errno);
455 		return -1;
456 	}
457 
458 	return 0;
459 }
460 
zlog_rule_output_stderr(zlog_rule_t * a_rule,zlog_thread_t * a_thread)461 static int zlog_rule_output_stderr(zlog_rule_t * a_rule,
462 				   zlog_thread_t * a_thread)
463 {
464 
465 	if (zlog_format_gen_msg(a_rule->format, a_thread)) {
466 		zc_error("zlog_format_gen_msg fail");
467 		return -1;
468 	}
469 
470 	if (write(STDERR_FILENO,
471 		zlog_buf_str(a_thread->msg_buf), zlog_buf_len(a_thread->msg_buf)) < 0) {
472 		zc_error("write fail, errno[%d]", errno);
473 		return -1;
474 	}
475 
476 	return 0;
477 }
478 /*******************************************************************************/
syslog_facility_atoi(char * facility)479 static int syslog_facility_atoi(char *facility)
480 {
481 	/* guess no unix system will choose -187
482 	 * as its syslog facility, so it is a safe return value
483 	 */
484 	zc_assert(facility, -187);
485 
486 	if (STRICMP(facility, ==, "LOG_LOCAL0")) return LOG_LOCAL0;
487 	if (STRICMP(facility, ==, "LOG_LOCAL1")) return LOG_LOCAL1;
488 	if (STRICMP(facility, ==, "LOG_LOCAL2")) return LOG_LOCAL2;
489 	if (STRICMP(facility, ==, "LOG_LOCAL3")) return LOG_LOCAL3;
490 	if (STRICMP(facility, ==, "LOG_LOCAL4")) return LOG_LOCAL4;
491 	if (STRICMP(facility, ==, "LOG_LOCAL5")) return LOG_LOCAL5;
492 	if (STRICMP(facility, ==, "LOG_LOCAL6")) return LOG_LOCAL6;
493 	if (STRICMP(facility, ==, "LOG_LOCAL7")) return LOG_LOCAL7;
494 	if (STRICMP(facility, ==, "LOG_USER")) return LOG_USER;
495 	if (STRICMP(facility, ==, "LOG_AUTHPRIV")) return LOG_AUTHPRIV;
496 	if (STRICMP(facility, ==, "LOG_CRON")) return LOG_CRON;
497 	if (STRICMP(facility, ==, "LOG_DAEMON")) return LOG_DAEMON;
498 	if (STRICMP(facility, ==, "LOG_FTP")) return LOG_FTP;
499 	if (STRICMP(facility, ==, "LOG_KERN")) return LOG_KERN;
500 	if (STRICMP(facility, ==, "LOG_LPR")) return LOG_LPR;
501 	if (STRICMP(facility, ==, "LOG_MAIL")) return LOG_MAIL;
502 	if (STRICMP(facility, ==, "LOG_NEWS")) return LOG_NEWS;
503 	if (STRICMP(facility, ==, "LOG_SYSLOG")) return LOG_SYSLOG;
504 		return LOG_AUTHPRIV;
505 
506 	zc_error("wrong syslog facility[%s], must in LOG_LOCAL[0-7] or LOG_USER", facility);
507 	return -187;
508 }
509 
zlog_rule_parse_path(char * path_start,char * path_str,size_t path_size,zc_arraylist_t ** path_specs,int * time_cache_count)510 static int zlog_rule_parse_path(char *path_start, /* start with a " */
511 		char *path_str, size_t path_size, zc_arraylist_t **path_specs,
512 		int *time_cache_count)
513 {
514 	char *p, *q;
515 	size_t len;
516 	zlog_spec_t *a_spec;
517 	zc_arraylist_t *specs;
518 
519 	p = path_start + 1;
520 
521 	q = strrchr(p, '"');
522 	if (!q) {
523 		zc_error("matching \" not found in conf line[%s]", path_start);
524 		return -1;
525 	}
526 	len = q - p;
527 	if (len > path_size - 1) {
528 		zc_error("file_path too long %ld > %ld", len, path_size - 1);
529 		return -1;
530 	}
531 	memcpy(path_str, p, len);
532 
533 	/* replace any environment variables like %E(HOME) */
534 	if (zc_str_replace_env(path_str, path_size)) {
535 		zc_error("zc_str_replace_env fail");
536 		return -1;
537 	}
538 
539 	if (strchr(path_str, '%') == NULL) {
540 		/* static, no need create specs */
541 		return 0;
542 	}
543 
544 	specs = zc_arraylist_new((zc_arraylist_del_fn)zlog_spec_del);
545 	if (!path_specs) {
546 		zc_error("zc_arraylist_new fail");
547 		return -1;
548 	}
549 
550 	for (p = path_str; *p != '\0'; p = q) {
551 		a_spec = zlog_spec_new(p, &q, time_cache_count);
552 		if (!a_spec) {
553 			zc_error("zlog_spec_new fail");
554 			goto err;
555 		}
556 
557 		if (zc_arraylist_add(specs, a_spec)) {
558 			zc_error("zc_arraylist_add fail");
559 			goto err;
560 		}
561 	}
562 
563 	*path_specs = specs;
564 	return 0;
565 err:
566 	if (specs) zc_arraylist_del(specs);
567 	if (a_spec) zlog_spec_del(a_spec);
568 	return -1;
569 }
570 
zlog_rule_new(char * line,zc_arraylist_t * levels,zlog_format_t * default_format,zc_arraylist_t * formats,unsigned int file_perms,size_t fsync_period,int * time_cache_count)571 zlog_rule_t *zlog_rule_new(char *line,
572 		zc_arraylist_t *levels,
573 		zlog_format_t * default_format,
574 		zc_arraylist_t * formats,
575 		unsigned int file_perms,
576 		size_t fsync_period,
577 		int * time_cache_count)
578 {
579 	int rc = 0;
580 	int nscan = 0;
581 	int nread = 0;
582 	zlog_rule_t *a_rule;
583 
584 	char selector[MAXLEN_CFG_LINE + 1];
585 	char category[MAXLEN_CFG_LINE + 1];
586 	char level[MAXLEN_CFG_LINE + 1];
587 
588 	char *action;
589 	char output[MAXLEN_CFG_LINE + 1];
590 	char format_name[MAXLEN_CFG_LINE + 1];
591 	char file_path[MAXLEN_CFG_LINE + 1];
592 	char archive_max_size[MAXLEN_CFG_LINE + 1];
593 	char *file_limit;
594 
595 	char *p;
596 	char *q;
597 	size_t len;
598 
599 	zc_assert(line, NULL);
600 	zc_assert(default_format, NULL);
601 	zc_assert(formats, NULL);
602 
603 	a_rule = calloc(1, sizeof(zlog_rule_t));
604 	if (!a_rule) {
605 		zc_error("calloc fail, errno[%d]", errno);
606 		return NULL;
607 	}
608 
609 	a_rule->file_perms = file_perms;
610 	a_rule->fsync_period = fsync_period;
611 
612 	/* line         [f.INFO "%H/log/aa.log", 20MB * 12; MyTemplate]
613 	 * selector     [f.INFO]
614 	 * *action      ["%H/log/aa.log", 20MB * 12; MyTemplate]
615 	 */
616 	memset(&selector, 0x00, sizeof(selector));
617 	nscan = sscanf(line, "%s %n", selector, &nread);
618 	if (nscan != 1) {
619 		zc_error("sscanf [%s] fail, selector", line);
620 		goto err;
621 	}
622 	action = line + nread;
623 
624 	/*
625 	 * selector     [f.INFO]
626 	 * category     [f]
627 	 * level        [.INFO]
628 	 */
629 	memset(category, 0x00, sizeof(category));
630 	memset(level, 0x00, sizeof(level));
631 	nscan = sscanf(selector, " %[^.].%s", category, level);
632 	if (nscan != 2) {
633 		zc_error("sscanf [%s] fail, category or level is null",
634 			 selector);
635 		goto err;
636 	}
637 
638 
639 	/* check and set category */
640 	for (p = category; *p != '\0'; p++) {
641 		if ((!isalnum(*p)) && (*p != '_') && (*p != '*') && (*p != '!')) {
642 			zc_error("category name[%s] character is not in [a-Z][0-9][_!*]",
643 				 category);
644 			goto err;
645 		}
646 	}
647 
648 	/* as one line can't be longer than MAXLEN_CFG_LINE, same as category */
649 	strcpy(a_rule->category, category);
650 
651 	/* check and set level */
652 	switch (level[0]) {
653 	case '=':
654 		/* aa.=debug */
655 		a_rule->compare_char = '=';
656 		p = level + 1;
657 		break;
658 	case '!':
659 		/* aa.!debug */
660 		a_rule->compare_char = '!';
661 		p = level + 1;
662 		break;
663 	case '*':
664 		/* aa.* */
665 		a_rule->compare_char = '*';
666 		p = level;
667 		break;
668 	default:
669 		/* aa.debug */
670 		a_rule->compare_char = '.';
671 		p = level;
672 		break;
673 	}
674 
675 	a_rule->level = zlog_level_list_atoi(levels, p);
676 
677 	/* level_bit is a bitmap represents which level can be output
678 	 * 32bytes, [0-255] levels, see level.c
679 	 * which bit field is 1 means allow output and 0 not
680 	 */
681 	switch (a_rule->compare_char) {
682 	case '=':
683 		memset(a_rule->level_bitmap, 0x00, sizeof(a_rule->level_bitmap));
684 		a_rule->level_bitmap[a_rule->level / 8] |= (1 << (7 - a_rule->level % 8));
685 		break;
686 	case '!':
687 		memset(a_rule->level_bitmap, 0xFF, sizeof(a_rule->level_bitmap));
688 		a_rule->level_bitmap[a_rule->level / 8] &= ~(1 << (7 - a_rule->level % 8));
689 		break;
690 	case '*':
691 		memset(a_rule->level_bitmap, 0xFF, sizeof(a_rule->level_bitmap));
692 		break;
693 	case '.':
694 		memset(a_rule->level_bitmap, 0x00, sizeof(a_rule->level_bitmap));
695 		a_rule->level_bitmap[a_rule->level / 8] |= ~(0xFF << (8 - a_rule->level % 8));
696 		memset(a_rule->level_bitmap + a_rule->level / 8 + 1, 0xFF,
697 				sizeof(a_rule->level_bitmap) -  a_rule->level / 8 - 1);
698 		break;
699 	}
700 
701 	/* action               ["%H/log/aa.log", 20MB * 12 ; MyTemplate]
702 	 * output               ["%H/log/aa.log", 20MB * 12]
703 	 * format               [MyTemplate]
704 	 */
705 	memset(output, 0x00, sizeof(output));
706 	memset(format_name, 0x00, sizeof(format_name));
707 	nscan = sscanf(action, " %[^;];%s", output, format_name);
708 	if (nscan < 1) {
709 		zc_error("sscanf [%s] fail", action);
710 		goto err;
711 	}
712 
713 	/* check and get format */
714 	if (STRCMP(format_name, ==, "")) {
715 		zc_debug("no format specified, use default");
716 		a_rule->format = default_format;
717 	} else {
718 		int i;
719 		int find_flag = 0;
720 		zlog_format_t *a_format;
721 
722 		zc_arraylist_foreach(formats, i, a_format) {
723 			if (zlog_format_has_name(a_format, format_name)) {
724 				a_rule->format = a_format;
725 				find_flag = 1;
726 				break;
727 			}
728 		}
729 		if (!find_flag) {
730 			zc_error("in conf file can't find format[%s], pls check",
731 			     format_name);
732 			goto err;
733 		}
734 	}
735 
736 	/* output               [-"%E(HOME)/log/aa.log" , 20MB*12]  [>syslog , LOG_LOCAL0 ]
737 	 * file_path            [-"%E(HOME)/log/aa.log" ]           [>syslog ]
738 	 * *file_limit          [20MB * 12 ~ "aa.#i.log" ]          [LOG_LOCAL0]
739 	 */
740 	memset(file_path, 0x00, sizeof(file_path));
741 	nscan = sscanf(output, " %[^,],", file_path);
742 	if (nscan < 1) {
743 		zc_error("sscanf [%s] fail", action);
744 		goto err;
745 	}
746 
747 	file_limit = strchr(output, ',');
748 	if (file_limit) {
749 		file_limit++; /* skip the , */
750 		while( isspace(*file_limit) ) {
751 			file_limit++;
752 		}
753 	}
754 
755 	p = NULL;
756 	switch (file_path[0]) {
757 	case '-' :
758 		/* sync file each time write log */
759 		if (file_path[1] != '"') {
760 			zc_error(" - must set before a file output");
761 			goto err;
762 		}
763 
764 		/* no need to fsync, as file is opened by O_SYNC, write immediately */
765 		a_rule->fsync_period = 0;
766 
767 		p = file_path + 1;
768 		a_rule->file_open_flags = O_SYNC;
769 		/* fall through */
770 	case '"' :
771 		if (!p) p = file_path;
772 
773 		rc = zlog_rule_parse_path(p, a_rule->file_path, sizeof(a_rule->file_path),
774 				&(a_rule->dynamic_specs), time_cache_count);
775 		if (rc) {
776 			zc_error("zlog_rule_parse_path fail");
777 			goto err;
778 		}
779 
780 		if (file_limit) {
781 			memset(archive_max_size, 0x00, sizeof(archive_max_size));
782 			nscan = sscanf(file_limit, " %[0-9MmKkBb] * %d ~",
783 					archive_max_size, &(a_rule->archive_max_count));
784 			if (nscan) {
785 				a_rule->archive_max_size = zc_parse_byte_size(archive_max_size);
786 			}
787 			p = strchr(file_limit, '"');
788 			if (p) { /* archive file path exist */
789 				rc = zlog_rule_parse_path(p,
790 					a_rule->archive_path, sizeof(a_rule->file_path),
791 					&(a_rule->archive_specs), time_cache_count);
792 				if (rc) {
793 					zc_error("zlog_rule_parse_path fail");
794 					goto err;
795 				}
796 
797 				p = strchr(a_rule->archive_path, '#');
798 				if ( (p == NULL) && (
799 						(strchr(p, 'r') == NULL) || (strchr(p, 's') == NULL)
800 					)
801 				   ) {
802 					zc_error("archive_path must contain #r or #s");
803 					goto err;
804 				}
805 			}
806 		}
807 
808 		/* try to figure out if the log file path is dynamic or static */
809 		if (a_rule->dynamic_specs) {
810 			if (a_rule->archive_max_size <= 0) {
811 				a_rule->output = zlog_rule_output_dynamic_file_single;
812 			} else {
813 				a_rule->output = zlog_rule_output_dynamic_file_rotate;
814 			}
815 		} else {
816 			struct stat stb;
817 
818 			if (a_rule->archive_max_size <= 0) {
819 				a_rule->output = zlog_rule_output_static_file_single;
820 			} else {
821 				/* as rotate, so need to reopen everytime */
822 				a_rule->output = zlog_rule_output_static_file_rotate;
823 			}
824 
825 			a_rule->static_fd = open(a_rule->file_path,
826 				O_WRONLY | O_APPEND | O_CREAT | a_rule->file_open_flags,
827 				a_rule->file_perms);
828 			if (a_rule->static_fd < 0) {
829 				zc_error("open file[%s] fail, errno[%d]", a_rule->file_path, errno);
830 				goto err;
831 			}
832 
833 			/* save off the inode information for checking for a changed file later on */
834 			if (fstat(a_rule->static_fd, &stb)) {
835 				zc_error("stat [%s] fail, errno[%d], failing to open static_fd", a_rule->file_path, errno);
836 				goto err;
837 			}
838 			a_rule->static_dev = stb.st_dev;
839 			a_rule->static_ino = stb.st_ino;
840 		}
841 		break;
842 	case '|' :
843 		a_rule->pipe_fp = popen(output + 1, "w");
844 		if (!a_rule->pipe_fp) {
845 			zc_error("popen fail, errno[%d]", errno);
846 			goto err;
847 		}
848 		a_rule->pipe_fd = fileno(a_rule->pipe_fp);
849 		if (a_rule->pipe_fd < 0 ) {
850 			zc_error("fileno fail, errno[%d]", errno);
851 			goto err;
852 		}
853 		a_rule->output = zlog_rule_output_pipe;
854 		break;
855 	case '>' :
856 		if (STRNCMP(file_path + 1, ==, "syslog", 6)) {
857 			a_rule->syslog_facility = syslog_facility_atoi(file_limit);
858 			if (a_rule->syslog_facility == -187) {
859 				zc_error("-187 get");
860 				goto err;
861 			}
862 			a_rule->output = zlog_rule_output_syslog;
863 			openlog(NULL, LOG_NDELAY | LOG_NOWAIT | LOG_PID, LOG_USER);
864 		} else if (STRNCMP(file_path + 1, ==, "stdout", 6)) {
865 			a_rule->output = zlog_rule_output_stdout;
866 		} else if (STRNCMP(file_path + 1, ==, "stderr", 6)) {
867 			a_rule->output = zlog_rule_output_stderr;
868 		} else {
869 			zc_error
870 			    ("[%s]the string after is not syslog, stdout or stderr", output);
871 			goto err;
872 		}
873 		break;
874 	case '$' :
875 		sscanf(file_path + 1, "%s", a_rule->record_name);
876 
877 		if (file_limit) {  /* record path exists */
878 			p = strchr(file_limit, '"');
879 			if (!p) {
880 				zc_error("record_path not start with \", [%s]", file_limit);
881 				goto err;
882 			}
883 			p++; /* skip 1st " */
884 
885 			q = strrchr(p, '"');
886 			if (!q) {
887 				zc_error("matching \" not found in conf line[%s]", p);
888 				goto err;
889 			}
890 			len = q - p;
891 			if (len > sizeof(a_rule->record_path) - 1) {
892 				zc_error("record_path too long %ld > %ld", len, sizeof(a_rule->record_path) - 1);
893 				goto err;
894 			}
895 			memcpy(a_rule->record_path, p, len);
896 		}
897 
898 		/* replace any environment variables like %E(HOME) */
899 		rc = zc_str_replace_env(a_rule->record_path, sizeof(a_rule->record_path));
900 		if (rc) {
901 			zc_error("zc_str_replace_env fail");
902 			goto err;
903 		}
904 
905 		/* try to figure out if the log file path is dynamic or static */
906 		if (strchr(a_rule->record_path, '%') == NULL) {
907 			a_rule->output = zlog_rule_output_static_record;
908 		} else {
909 			zlog_spec_t *a_spec;
910 
911 			a_rule->output = zlog_rule_output_dynamic_record;
912 
913 			a_rule->dynamic_specs = zc_arraylist_new((zc_arraylist_del_fn)zlog_spec_del);
914 			if (!(a_rule->dynamic_specs)) {
915 				zc_error("zc_arraylist_new fail");
916 				goto err;
917 			}
918 			for (p = a_rule->record_path; *p != '\0'; p = q) {
919 				a_spec = zlog_spec_new(p, &q, time_cache_count);
920 				if (!a_spec) {
921 					zc_error("zlog_spec_new fail");
922 					goto err;
923 				}
924 
925 				rc = zc_arraylist_add(a_rule->dynamic_specs, a_spec);
926 				if (rc) {
927 					zlog_spec_del(a_spec);
928 					zc_error("zc_arraylist_add fail");
929 					goto err;
930 				}
931 			}
932 		}
933 		break;
934 	default :
935 		zc_error("the 1st char[%c] of file_path[%s] is wrong",
936 		       file_path[0], file_path);
937 		goto err;
938 	}
939 
940 	//zlog_rule_profile(a_rule, ZC_DEBUG);
941 	return a_rule;
942 err:
943 	zlog_rule_del(a_rule);
944 	return NULL;
945 }
946 
zlog_rule_del(zlog_rule_t * a_rule)947 void zlog_rule_del(zlog_rule_t * a_rule)
948 {
949 	zc_assert(a_rule,);
950 	if (a_rule->dynamic_specs) {
951 		zc_arraylist_del(a_rule->dynamic_specs);
952 		a_rule->dynamic_specs = NULL;
953 	}
954 	if (a_rule->static_fd) {
955 		if (close(a_rule->static_fd)) {
956 			zc_error("close fail, maybe cause by write, errno[%d]", errno);
957 		}
958 	}
959 	if (a_rule->pipe_fp) {
960 		if (pclose(a_rule->pipe_fp) == -1) {
961 			zc_error("pclose fail, errno[%d]", errno);
962 		}
963 	}
964 	if (a_rule->archive_specs) {
965 		zc_arraylist_del(a_rule->archive_specs);
966 		a_rule->archive_specs = NULL;
967 	}
968 	free(a_rule);
969 	zc_debug("zlog_rule_del[%p]", a_rule);
970 	return;
971 }
972 
973 /*******************************************************************************/
zlog_rule_output(zlog_rule_t * a_rule,zlog_thread_t * a_thread)974 int zlog_rule_output(zlog_rule_t * a_rule, zlog_thread_t * a_thread)
975 {
976 	switch (a_rule->compare_char) {
977 	case '*' :
978 		return a_rule->output(a_rule, a_thread);
979 		break;
980 	case '.' :
981 		if (a_thread->event->level >= a_rule->level) {
982 			return a_rule->output(a_rule, a_thread);
983 		} else {
984 			return 0;
985 		}
986 		break;
987 	case '=' :
988 		if (a_thread->event->level == a_rule->level) {
989 			return a_rule->output(a_rule, a_thread);
990 		} else {
991 			return 0;
992 		}
993 		break;
994 	case '!' :
995 		if (a_thread->event->level != a_rule->level) {
996 			return a_rule->output(a_rule, a_thread);
997 		} else {
998 			return 0;
999 		}
1000 		break;
1001 	}
1002 
1003 	return 0;
1004 }
1005 
1006 /*******************************************************************************/
zlog_rule_is_wastebin(zlog_rule_t * a_rule)1007 int zlog_rule_is_wastebin(zlog_rule_t * a_rule)
1008 {
1009 	zc_assert(a_rule, -1);
1010 
1011 	if (STRCMP(a_rule->category, ==, "!")) {
1012 		return 1;
1013 	}
1014 
1015 	return 0;
1016 }
1017 
1018 /*******************************************************************************/
zlog_rule_match_category(zlog_rule_t * a_rule,char * category)1019 int zlog_rule_match_category(zlog_rule_t * a_rule, char *category)
1020 {
1021 	zc_assert(a_rule, -1);
1022 	zc_assert(category, -1);
1023 
1024 	if (STRCMP(a_rule->category, ==, "*")) {
1025 		/* '*' match anything, so go on */
1026 		return 1;
1027 	} else if (STRCMP(a_rule->category, ==, category)) {
1028 		/* accurate compare */
1029 		return 1;
1030 	} else {
1031 		/* aa_ match aa_xx & aa, but not match aa1_xx */
1032 		size_t len;
1033 		len = strlen(a_rule->category);
1034 
1035 		if (a_rule->category[len - 1] == '_') {
1036 			if (strlen(category) == len - 1) {
1037 				len--;
1038 			}
1039 
1040 			if (STRNCMP(a_rule->category, ==, category, len)) {
1041 				return 1;
1042 			}
1043 		}
1044 	}
1045 
1046 	return 0;
1047 }
1048 
1049 /*******************************************************************************/
1050 
zlog_rule_set_record(zlog_rule_t * a_rule,zc_hashtable_t * records)1051 int zlog_rule_set_record(zlog_rule_t * a_rule, zc_hashtable_t *records)
1052 {
1053 	zlog_record_t *a_record;
1054 
1055 	if (a_rule->output != zlog_rule_output_static_record
1056 	&&  a_rule->output != zlog_rule_output_dynamic_record) {
1057 		return 0; /* fliter, may go through not record rule */
1058 	}
1059 
1060 	a_record = zc_hashtable_get(records, a_rule->record_name);
1061 	if (a_record) {
1062 		a_rule->record_func = a_record->output;
1063 	}
1064 	return 0;
1065 }
1066