int test_text_split(test_case *test) { int i, rc; char **token; Vector tokens; rc = 0; printf("s=\"%s\" d=\"%s\" f=0x%02X ", test->string, test->delims, test->flags); tokens = TextSplit(test->string, test->delims, test->flags); if (tokens == NULL) { return -1; } printf("e=%d l=%ld ", test->expect_length, VectorLength(tokens)); if (test->expect_length != VectorLength(tokens)) { rc = -1; } for (i = 0, token = (char **)VectorBase(tokens); *token != NULL; token++, i++) { if (test->expect_items[i] == NULL || strcmp(*token, test->expect_items[i]) != 0) rc = -1; printf("[%s]", *token); } if (test->expect_items[i] != NULL) rc = -1; printf("... %s\n", rc == 0 ? "OK" : "FAIL"); VectorDestroy(tokens); return rc; }
int main(int argc, char **argv) { Vector v; char *delims; int i, ch, argi, flags; flags = 0; delims = NULL; while ((ch = getopt(argc, argv, "d:f:t")) != -1) { switch (ch) { case 'd': delims = optarg; break; case 'f': flags = (int) strtol(optarg, NULL, 0); break; case 't': return test_suite(); default: optind = argc; } } if (argc <= optind) { (void) fputs(usage, stderr); return EXIT_FAILURE; } for (argi = optind; argi < argc; argi++) { if ((v = TextSplit(argv[argi], delims, flags)) == NULL) { (void) fprintf(stderr, "out of memory\n"); return EXIT_FAILURE; } for (i = 0; i < VectorLength(v); i++) (void) printf("%s\n", (char *)VectorGet(v, i)); VectorDestroy(v); } return EXIT_SUCCESS; }
/* * Pattern Rule Grammar * -------------------- * * line := blank | "#" comment | rule * * rule := pattern *[ whitespace ] actions * * whitespace := SPACE | TAB * * pattern := "/" extended_regex "/" * * actions := action [ ";" actions ] * * action := thread | limit | report | on_exceed | on_expire * * quote := "'" | '"' * * index := number | quote text quote * * indices := index [ "," index ] * * thread := "t=" indices * * limit := "l=" indices "," max "/" period [ unit ] * * report := "r=" mail *[ "," mail ] * * on_exceed := "c=" quoted_shell_command * * on_expire := "C=" quoted_shell_command * * quoted_shell_command := quote text quote */ static int init_rule(char *line) { Vector fields; const char *next; struct pattern_rule *rule; int i, rc = -1, err; char *field, error[128]; if (*line != '/') goto error0; if ((rule = calloc(1, sizeof (*rule))) == NULL) goto error0; if ((rule->pattern = TokenNext(line, &next, "/", TOKEN_KEEP_ASIS)) == NULL) goto error1; if ((fields = TextSplit(next, ";", TOKEN_KEEP_ASIS)) == NULL) goto error1; if ((err = regcomp(&rule->re, rule->pattern, REG_EXTENDED|REG_NEWLINE)) != 0) { (void) regerror(err, &rule->re, error, sizeof (error)); (void) fprintf(stderr, "pattern /%s/: %s (%d)\n", rule->pattern, error, err); goto error2; } /* Assume new previously unknown pattern. */ if ((err = sqlite3_bind_text(ctx.insert_pattern, 1, rule->pattern, -1, SQLITE_STATIC)) != SQLITE_OK) { sql_error(__FILE__, __LINE__, ctx.insert_pattern); goto error2; } if ((err = sql_step(ctx.db, ctx.insert_pattern)) != SQLITE_DONE) { sql_error(__FILE__, __LINE__, ctx.insert_pattern); goto error2; } rule->id_pattern = sqlite3_last_insert_rowid(ctx.db); (void) sqlite3_clear_bindings(ctx.insert_pattern); sqlite3_reset(ctx.insert_pattern); /* Last insert rowid is zero if no row was inserted, thus it * already exists and we need to find the pattern number (OID). */ if (rule->id_pattern == 0) { if ((err = sqlite3_bind_text(ctx.find_pattern, 1, rule->pattern, -1, SQLITE_STATIC)) != SQLITE_OK) { sql_error(__FILE__, __LINE__, ctx.find_pattern); goto error2; } if ((err = sql_step(ctx.db, ctx.find_pattern)) != SQLITE_ROW) { sql_error(__FILE__, __LINE__, ctx.find_pattern); goto error2; } if ((rule->id_pattern = sqlite3_column_int64(ctx.find_pattern, 0)) == 0) { sql_error(__FILE__, __LINE__, ctx.find_pattern); goto error2; } (void) sqlite3_clear_bindings(ctx.find_pattern); sqlite3_reset(ctx.find_pattern); } for (i = 0; i < VectorLength(fields); i++) { field = VectorGet(fields, i); field += strspn(field, " \t"); switch (*field) { case 'c': if (field[1] == '=') { free(rule->on_exceed); rule->on_exceed = VectorReplace(fields, i, NULL); } break; case 'C': if (field[1] == '=') { free(rule->on_expire); rule->on_expire = VectorReplace(fields, i, NULL); } break; case 'r': if (field[1] == '=') { free(rule->report); rule->report = VectorReplace(fields, i, NULL); } break; case 't': if (field[1] == '=') rule->thread = strtol(field+2, NULL, 10); break; case 'l': if (field[1] == '=') continue; } (void) VectorRemove(fields, i--); } field[strcspn(field, "\r\n")] = '\0'; /* What remains of fields should be an empty vector or an array of l= actions. */ rule->limits = (char **) VectorBase(fields); fields = NULL; rule->node.data = rule; rule->node.free = free_rule; listInsertAfter(&pattern_rules, NULL, &rule->node); rc = 0; error2: VectorDestroy(fields); error1: if (rc != 0) free_rule(rule); error0: return rc; }
static void limit_report(struct pattern_rule *rule, const char *action, struct limit *limit, const char *line, regmatch_t *parens) { SMTP2 *smtp; Vector rcpts; int smtp_flags; const unsigned char *text; char **table, buffer[SMTP_PATH_LENGTH]; if (0 < debug) (void) fprintf( stdout, "/%s/ %s: limit exceeded (%d)\n", rule->pattern, action, limit->reported + limit->counter ); smtp = NULL; rcpts = NULL; if (report_to != NULL) { rcpts = TextSplit(rule->report != NULL ? strchr(rule->report, '=')+1 : report_to, ",; ", 0); if (VectorLength(rcpts) <= 0) { (void) fprintf(stderr, "no report-to mail addresses\n"); goto error0; } /* Try to connect to local smart host. */ smtp_flags = SMTP_FLAG_LOG | (1 < debug ? SMTP_FLAG_DEBUG : 0); smtp = smtp2Open(smtp_host, SMTP_CONNECT_TO * UNIT_MILLI, SMTP_COMMAND_TO * UNIT_MILLI, smtp_flags); if (smtp == NULL) { (void) fprintf(stderr, "%s: %s (%d)\n", smtp_host, strerror(errno), errno); goto error1; } if (smtp2Mail(smtp, report_from) != SMTP_OK) { (void) fprintf(stderr, "%s: null sender not accepted\n", smtp_host); goto error2; } for (table = (char **) VectorBase(rcpts); *table != NULL; table++) { if (smtp2Rcpt(smtp, *table) != SMTP_OK) goto error2; } TimeStamp(&smtp->start, buffer, sizeof (buffer)); (void) smtp2Printf(smtp, "Date: %s" CRLF, buffer); (void) smtp2Printf(smtp, "From: \"%s\" <%s>" CRLF, _NAME, smtp->sender); (void) smtp2Printf(smtp, "Message-ID: <%s@%s>" CRLF, smtp->id_string, smtp->local_ip); (void) smtp2Printf(smtp, "Subject: %s log limit exceeded %s" CRLF, _NAME, limit->token); (void) smtp2Print(smtp, CRLF, sizeof (CRLF)-1); (void) smtp2Printf( smtp, "/%s/ %s: limit exceeded (%d)" CRLF CRLF, rule->pattern, action, limit->reported + limit->counter ); } if (sqlite3_bind_int64(ctx.select_limit_to_log, 1, limit->id_limit) != SQLITE_OK) { sql_error(__FILE__, __LINE__, ctx.increment_limit); goto error2; } if (sqlite3_bind_int64(ctx.select_limit_to_log, 2, limit->updated) != SQLITE_OK) { sql_error(__FILE__, __LINE__, ctx.increment_limit); goto error2; } while (sql_step(ctx.db, ctx.select_limit_to_log) == SQLITE_ROW) { if ((text = sqlite3_column_text(ctx.select_limit_to_log, 0)) == NULL) continue; if (0 < debug) (void) fprintf(stdout, "\t%s" CRLF, text); if (smtp != NULL) (void) smtp2Printf(smtp, "%s" CRLF, text); } if (rule->on_exceed != NULL) do_command(rule, rule->on_exceed, line, parens); (void) sqlite3_clear_bindings(ctx.select_limit_to_log); sqlite3_reset(ctx.select_limit_to_log); if (smtp != NULL) (void) smtp2Dot(smtp); error2: smtp2Close(smtp); error1: VectorDestroy(rcpts); error0: ; }
int main(int argc, char **argv) { int ch, argi; while ((ch = getopt(argc, argv, options)) != -1) { switch (ch) { case '1': first_match_only = 1; break; case 'f': follow_flag = 1; break; case 'F': report_from = optarg; if (*report_from == '\0') report_from = NULL; break; case 'v': debug++; break; case 'd': db_path = optarg; break; case 'D': db_delete = 1; break; case 'r': report_to = optarg; break; case 'R': db_reset = 1; break; case 's': smtp_host = optarg; break; case 'y': assumed_year = strtol(optarg, NULL, 10); break; case 'z': assumed_tz = strtol(optarg, NULL, 10); break; default: (void) printf(usage); return EX_USAGE; } } if (argc <= optind || (follow_flag && optind+2 < argc)) { (void) printf(usage); return EX_USAGE; } if (1 < debug) LogOpen(NULL); if (db_path == NULL) { (void) snprintf(db_user, sizeof (db_user), DB_PATH_FMT, getuid()); db_path = db_user; } if (db_delete) (void) unlink(db_path); init_today(); if (atexit(at_exit_cleanup) || init_db(db_path) || init_rules(argv[optind])) return EX_SOFTWARE; if ((report = TextSplit(report_to, ", ", 0)) == NULL) return EX_SOFTWARE; if (optind+1 == argc) { follow_flag = 0; process_file("-"); } else { for (argi = optind+1; argi < argc; argi++) process_file(argv[argi]); } return EXIT_SUCCESS; }