void readAslRow(aslmsg row, Row& r) { pt::ptree extras; // Fetch each column individually, adding it to the result map size_t i = 0; for (const char* key = asl_key(row, i); key != nullptr; key = asl_key(row, ++i)) { const char* val = asl_get(row, key); // Rarely, asl_fetch_key_val_op will return a NULL pointer for // key/value, so we defend against that case by using the empty string std::string key_s = key != nullptr ? std::string(key) : ""; std::string val_s = val != nullptr ? std::string(val) : ""; if (kAslKeyToColumnMap.count(key_s) > 0) { // This key is a default column r[kAslKeyToColumnMap.at(key_s)] = val_s; } else { // This key is not a default column, add it to extras extras.push_back(pt::ptree::value_type(key_s, pt::ptree(val_s))); } } // Join up the extras and add them to the Extra column std::stringstream ss; pt::write_json(ss, extras, false); r[kExtraColumnKey] = ss.str(); }
/* qsort compare function for sorting by message ID */ static int sort_compare(const void *a, const void *b) { const char *va, *vb; uint64_t na, nb; va = asl_get(*(aslmsg *)a, ASL_KEY_MSG_ID); vb = asl_get(*(aslmsg *)b, ASL_KEY_MSG_ID); if (va == NULL) return -1; if (vb == NULL) return 1; na = atoll(va); nb = atoll(vb); if (na < nb) return -1; if (na > nb) return 1; return 0; }
static int _bsd_send(aslmsg msg, struct config_rule *r, char **out, char **fwd, time_t now) { char *sf, *outmsg; const char *vlevel, *vfacility; size_t outlen; int pf, fc, status, is_dup, do_write; uint32_t msg_hash, n; if (out == NULL) return -1; if (fwd == NULL) return -1; if (r == NULL) return -1; _syslog_dst_open(r); if (r->type == DST_TYPE_NOTE) { notify_post(r->dst+1); return 0; } msg_hash = 0; outmsg = NULL; /* Build output string if it hasn't been built by a previous rule-match */ if (*out == NULL) { *out = asl_format_message((asl_msg_t *)msg, ASL_MSG_FMT_BSD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &n); if (*out == NULL) return -1; } /* check if message is a duplicate of the last message, and inside the dup time window */ is_dup = 0; if ((global.bsd_max_dup_time > 0) && (*out != NULL) && (r->last_msg != NULL)) { msg_hash = asl_core_string_hash(*out + 16, strlen(*out + 16)); if ((r->last_hash == msg_hash) && (!strcmp(r->last_msg, *out + 16))) { if ((now - r->last_time) < global.bsd_max_dup_time) is_dup = 1; } } if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK)) { pf = 7; vlevel = asl_get(msg, ASL_KEY_LEVEL); if (vlevel != NULL) pf = atoi(vlevel); fc = asl_syslog_faciliy_name_to_num(asl_get(msg, ASL_KEY_FACILITY)); if (fc > 0) pf |= fc; sf = NULL; asprintf(&sf, "<%d>%s", pf, *out); if (sf == NULL) return -1; *fwd = sf; } if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd); else outlen = strlen(*out); if ((r->type == DST_TYPE_FILE) || (r->type == DST_TYPE_CONS)) { /* * If current message is NOT a duplicate and r->last_count > 0 * we need to write a "last message was repeated N times" log entry */ if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _bsd_send_repeat_msg(r); do_write = 1; /* * Special case for kernel messages. * Don't write kernel messages to /dev/console. * The kernel printf routine already sends them to /dev/console * so writing them here would cause duplicates. */ vfacility = asl_get(msg, ASL_KEY_FACILITY); if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) do_write = 0; if ((do_write == 1) && (r->type == DST_TYPE_FILE) && (is_dup == 1)) { do_write = 0; if (r->dup_timer == NULL) { /* create a timer to flush dups on this file */ r->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, bsd_out_queue); dispatch_source_set_event_handler(r->dup_timer, ^{ _bsd_send_repeat_msg(r); }); }
/* save a message to an appropriately named BB file */ static uint32_t save_bb_msg(aslmsg msg) { const char *val; uid_t u, ruid; gid_t g, rgid; struct tm ctm; time_t msg_time, bb; char *path, *tstring; asl_file_t *out; uint64_t mid; mode_t m; uint32_t status; if (msg == NULL) return ASL_STATUS_OK; val = asl_get(msg, ASL_KEY_EXPIRE_TIME); if (val == NULL) return ASL_STATUS_INVALID_ARG; msg_time = asl_parse_time(val); val = asl_get(msg, ASL_KEY_READ_UID); ruid = -1; if (val != NULL) ruid = atoi(val); val = asl_get(msg, ASL_KEY_READ_GID); rgid = -1; if (val != NULL) rgid = atoi(val); if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; /* * This supports 12 monthy "Best Before" buckets. * We advance the actual expiry time to day zero of the following month. * mktime() is clever enough to know that you actually mean the last day * of the previous month. What we get back from localtime is the last * day of the month in which the message expires, which we use in the name. */ ctm.tm_sec = 0; ctm.tm_min = 0; ctm.tm_hour = 0; ctm.tm_mday = 0; ctm.tm_mon += 1; bb = mktime(&ctm); u = 0; g = 0; if (ruid != -1) u = ruid; if (rgid != -1) g = rgid; out = NULL; if (cache_file != NULL) { if ((cache_uid == u) && (cache_gid == g) && (cache_bb == bb)) { out = cache_file; } else { asl_file_close(cache_file); cache_file = NULL; cache_uid = -1; cache_gid = -1; cache_bb = 0; } } if (out == NULL) { if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; tstring = NULL; asprintf(&tstring, "%s/BB.%d.%02d.%02d", store_path, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); if (tstring == NULL) return ASL_STATUS_NO_MEMORY; path = NULL; m = 0644; if (ruid == -1) { if (rgid == -1) { asprintf(&path, "%s.asl", tstring); } else { m = 0640; asprintf(&path, "%s.G%d.asl", tstring, g); } } else { if (rgid == -1) { m = 0600; asprintf(&path, "%s.U%d.asl", tstring, u); } else { m = 0640; asprintf(&path, "%s.U%d.G%u.asl", tstring, u, g); } } if (path == NULL) return ASL_STATUS_NO_MEMORY; status = asl_file_open_write(path, m, u, g, &out); free(path); if (status != ASL_STATUS_OK) return status; if (out == NULL) return ASL_STATUS_FAILED; out->flags = ASL_FILE_FLAG_PRESERVE_MSG_ID; cache_file = out; cache_uid = u; cache_gid = g; cache_bb = bb; } status = asl_file_save(out, msg, &mid); return status; }
/* remove all messages that have an ASLExpireTime key */ static uint32_t do_ASLExpireTime_filter(const char *name) { aslmsg msg; asl_file_t *in, *out; uint32_t status; uint64_t mid; char *inpath, *outpath; struct stat sb; if (name == NULL) return ASL_STATUS_INVALID_ARG; in = NULL; inpath = NULL; asprintf(&inpath, "%s/%s", store_path, name); if (inpath == NULL) return ASL_STATUS_NO_MEMORY; memset(&sb, 0, sizeof(struct stat)); if (stat(inpath, &sb) < 0) { free(inpath); return ASL_STATUS_INVALID_STORE; } status = asl_file_open_read(inpath, &in); if (status != ASL_STATUS_OK) { free(inpath); return ASL_STATUS_OK; } out = NULL; outpath = NULL; asprintf(&outpath, "%s/%s", store_path, TEMP_NAME); if (outpath == NULL) { asl_file_close(in); free(inpath); return ASL_STATUS_NO_MEMORY; } status = asl_file_open_write(outpath, sb.st_mode, sb.st_uid, sb.st_gid, &out); if (status != ASL_STATUS_OK) { asl_file_close(in); free(inpath); free(outpath); return status; } out->flags = ASL_FILE_FLAG_PRESERVE_MSG_ID; msg = NULL; while (asl_file_fetch_next(in, &msg) == ASL_STATUS_OK) { if (msg == NULL) break; mid = 0; if (asl_get(msg, ASL_KEY_EXPIRE_TIME) == NULL) status = asl_file_save(out, msg, &mid); asl_free(msg); msg = NULL; if (status != ASL_STATUS_OK) break; } asl_file_close(in); asl_file_close(out); unlink(inpath); rename(outpath, inpath); free(inpath); free(outpath); return status; }
uint32_t asl_store_open_aux(asl_store_t *s, aslmsg msg, int *out_fd, char **url) { struct tm ctm; time_t msg_time, bb; char *path, *dir, *tstring; const char *val; uid_t ruid, u; gid_t rgid, g; mode_t m; uint32_t status; uint64_t fid; int fd; if (s == NULL) return ASL_STATUS_INVALID_STORE; if (msg == NULL) return ASL_STATUS_INVALID_ARG; if (out_fd == NULL) return ASL_STATUS_INVALID_ARG; if (url == NULL) return ASL_STATUS_INVALID_ARG; msg_time = time(NULL); val = asl_get(msg, ASL_KEY_READ_UID); ruid = -1; if (val != NULL) ruid = atoi(val); val = asl_get(msg, ASL_KEY_READ_GID); rgid = -1; if (val != NULL) rgid = atoi(val); bb = 0; val = asl_get(msg, ASL_KEY_EXPIRE_TIME); if (val != NULL) { bb = 1; msg_time = asl_parse_time(val); } if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; dir = NULL; if (bb == 1) { /* * This supports 12 monthly "Best Before" buckets. * We advance the actual expiry time to day zero of the following month. * mktime() is clever enough to know that you actually mean the last day * of the previous month. What we get back from localtime is the last * day of the month in which the message expires, which we use in the name. */ ctm.tm_sec = 0; ctm.tm_min = 0; ctm.tm_hour = 0; ctm.tm_mday = 0; ctm.tm_mon += 1; bb = mktime(&ctm); if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; asprintf(&dir, "BB.AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); } else { asprintf(&dir, "AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); } if (dir == NULL) return ASL_STATUS_NO_MEMORY; status = asl_store_mkdir(s, dir, 0755); if (status != ASL_STATUS_OK) { free(dir); return status; } fid = s->next_id; s->next_id++; tstring = NULL; asprintf(&tstring, "%s/%llu", dir, fid); free(dir); if (tstring == NULL) return ASL_STATUS_NO_MEMORY; u = 0; g = 0; m = 0644; path = asl_store_make_ug_path(s->base_dir, tstring, NULL, ruid, rgid, &u, &g, &m); free(tstring); if (path == NULL) return ASL_STATUS_NO_MEMORY; fd = asl_file_create(path, u, g, m); if (fd < 0) { free(path); *out_fd = -1; return ASL_STATUS_WRITE_FAILED; } /* URL is file://<path> */ *url = NULL; asprintf(url, "file://%s", path); free(path); *out_fd = fd; return status; }
uint32_t asl_store_save(asl_store_t *s, aslmsg msg) { struct tm ctm; time_t msg_time, now, bb; char *path, *tmp_path, *tstring, *scratch; const char *val; uid_t ruid; gid_t rgid; asl_file_t *f; uint32_t status, check_cache, trigger_aslmanager, len; uint64_t xid, ftime; size_t fsize; if (s == NULL) return ASL_STATUS_INVALID_STORE; if (msg == NULL) return ASL_STATUS_INVALID_ARG; now = time(NULL); check_cache = 0; if ((s->last_write + FILE_CACHE_TTL) <= now) check_cache = 1; trigger_aslmanager = 0; msg_time = 0; val = asl_get(msg, ASL_KEY_TIME); if (val == NULL) msg_time = now; else msg_time = asl_parse_time(val); if (msg_time >= s->start_tomorrow) { if (now >= s->start_tomorrow) { /* new day begins */ check_cache = 0; asl_store_file_closeall(s); /* * _asl_start_today should never fail, but if it does, * just push forward one day. That will probably be correct, and if * it isn't, the next message that gets saved will push it ahead again * until we get to the right date. */ s->start_today = _asl_start_today(); if (s->start_today == 0) s->start_today = s->start_tomorrow; s->start_tomorrow = s->start_today + SECONDS_PER_DAY; } } val = asl_get(msg, ASL_KEY_READ_UID); ruid = -1; if (val != NULL) ruid = atoi(val); val = asl_get(msg, ASL_KEY_READ_GID); rgid = -1; if (val != NULL) rgid = atoi(val); bb = 0; val = asl_get(msg, ASL_KEY_EXPIRE_TIME); if (val != NULL) { bb = 1; msg_time = asl_parse_time(val); } if (fseeko(s->storedata, 0, SEEK_SET) != 0) return ASL_STATUS_WRITE_FAILED; xid = asl_core_htonq(s->next_id); if (fwrite(&xid, sizeof(uint64_t), 1, s->storedata) != 1) return ASL_STATUS_WRITE_FAILED; /* flush data */ fflush(s->storedata); xid = s->next_id; s->next_id++; s->last_write = now; if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; tstring = NULL; if (bb == 1) { /* * This supports 12 monthly "Best Before" buckets. * We advance the actual expiry time to day zero of the following month. * mktime() is clever enough to know that you actually mean the last day * of the previous month. What we get back from localtime is the last * day of the month in which the message expires, which we use in the name. */ ctm.tm_sec = 0; ctm.tm_min = 0; ctm.tm_hour = 0; ctm.tm_mday = 0; ctm.tm_mon += 1; bb = mktime(&ctm); if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; asprintf(&tstring, "BB.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); } else { asprintf(&tstring, "%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); } if (tstring == NULL) return ASL_STATUS_NO_MEMORY; status = asl_store_file_open_write(s, tstring, ruid, rgid, bb, &f, now, check_cache); free(tstring); tstring = NULL; if (status != ASL_STATUS_OK) return status; status = asl_file_save(f, msg, &xid); if (status != ASL_STATUS_OK) return status; fsize = asl_file_size(f); ftime = asl_file_ctime(f); /* if file is larger than max_file_size, rename it and trigger aslmanager */ if ((s->max_file_size != 0) && (fsize > s->max_file_size)) { trigger_aslmanager = 1; status = ASL_STATUS_OK; path = asl_store_file_path(s, f); asl_store_file_close(s, f); if (path != NULL) { tmp_path = NULL; len = strlen(path); if ((len >= 4) && (!strcmp(path + len - 4, ".asl"))) { /* rename xxxxxxx.asl to xxxxxxx.timestamp.asl */ scratch = strdup(path); if (scratch != NULL) { scratch[len - 4] = '\0'; asprintf(&tmp_path, "%s.%llu.asl", scratch, ftime); free(scratch); } } else { /* append timestamp */ asprintf(&tmp_path, "%s.%llu", path, ftime); } if (tmp_path == NULL) { status = ASL_STATUS_NO_MEMORY; } else { if (rename(path, tmp_path) != 0) status = ASL_STATUS_FAILED; free(tmp_path); } free(path); } } if (trigger_aslmanager != 0) asl_trigger_aslmanager(); return status; }