/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* Fluent Bit * ========== * Copyright (C) 2023 The Fluent Bit Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "we.h" #include "we_wmi.h" #include "we_wmi_service.h" #include "we_util.h" #include "we_metric.h" static double nop_adjust(double value) { return value; } static int construct_include_clause(struct flb_we *ctx, flb_sds_t *clause) { int ret = -1; size_t off = 0; msgpack_unpacked result; msgpack_object key; msgpack_object val; msgpack_object map; int map_size; int i; int idx = 0; int use_like = FLB_FALSE; char *key_str = NULL; size_t key_str_size = 0; char *val_str = NULL; size_t val_str_size = 0; flb_sds_t val_buf; msgpack_unpacked_init(&result); while (msgpack_unpack_next(&result, ctx->service_include_buffer, ctx->service_include_buffer_size, &off) == MSGPACK_UNPACK_SUCCESS) { if (result.data.type != MSGPACK_OBJECT_MAP) { flb_plg_error(ctx->ins, "Invalid include buffer"); ret = -2; goto cleanup; } map = result.data; map_size = map.via.map.size; for (i = 0; i < map_size; i++) { use_like = FLB_FALSE; if (idx == 0) { flb_sds_cat_safe(clause, "(", 1); } else { flb_sds_cat_safe(clause, " OR ", 4); } key = map.via.map.ptr[i].key; val = map.via.map.ptr[i].val; if (key.type == MSGPACK_OBJECT_BIN) { key_str = (char *) key.via.bin.ptr; key_str_size = key.via.bin.size; } else if (key.type == MSGPACK_OBJECT_STR) { key_str = (char *) key.via.str.ptr; key_str_size = key.via.str.size; } if (val.type == MSGPACK_OBJECT_BIN) { val_str = (char *) val.via.bin.ptr; val_str_size = val.via.bin.size; val_buf = flb_sds_create_len(val_str, val_str_size); if (val_buf == NULL) { flb_plg_error(ctx->ins, "val_buf creation is failed"); ret = -3; goto cleanup; } } else if (val.type == MSGPACK_OBJECT_STR) { val_str = (char *) val.via.str.ptr; val_str_size = val.via.str.size; val_buf = flb_sds_create_len(val_str, val_str_size); if (val_buf == NULL) { flb_plg_error(ctx->ins, "val_buf creation is failed"); ret = -3; goto cleanup; } } if (val_str != NULL && strstr(val_buf, "%") != NULL) { use_like = FLB_TRUE; flb_sds_destroy(val_buf); } flb_sds_cat_safe(clause, key_str, key_str_size); if (use_like == FLB_TRUE) { flb_sds_cat_safe(clause, " LIKE ", 6); } else { flb_sds_cat_safe(clause, "=", 1); } flb_sds_cat_safe(clause, "'", 1); flb_sds_cat_safe(clause, val_str, val_str_size); flb_sds_cat_safe(clause, "'", 1); idx++; } flb_sds_cat_safe(clause, ")", 1); } msgpack_unpacked_destroy(&result); return 0; cleanup: msgpack_unpacked_destroy(&result); return ret; } static int construct_exclude_clause(struct flb_we *ctx, flb_sds_t *clause) { int ret = -1; size_t off = 0; msgpack_unpacked result; msgpack_object key; msgpack_object val; msgpack_object map; int map_size; int i; int idx = 0; int use_like = FLB_FALSE; char *key_str = NULL; size_t key_str_size = 0; char *val_str = NULL; size_t val_str_size = 0; flb_sds_t val_buf; msgpack_unpacked_init(&result); while (msgpack_unpack_next(&result, ctx->service_exclude_buffer, ctx->service_exclude_buffer_size, &off) == MSGPACK_UNPACK_SUCCESS) { if (result.data.type != MSGPACK_OBJECT_MAP) { flb_plg_error(ctx->ins, "Invalid exclude buffer"); ret = -2; goto cleanup; } map = result.data; map_size = map.via.map.size; for (i = 0; i < map_size; i++) { use_like = FLB_FALSE; if (idx == 0) { flb_sds_cat_safe(clause, "(", 1); } else { flb_sds_cat_safe(clause, " AND ", 5); } key = map.via.map.ptr[i].key; val = map.via.map.ptr[i].val; if (key.type == MSGPACK_OBJECT_BIN) { key_str = (char *) key.via.bin.ptr; key_str_size = key.via.bin.size; } else if (key.type == MSGPACK_OBJECT_STR) { key_str = (char *) key.via.str.ptr; key_str_size = key.via.str.size; } if (val.type == MSGPACK_OBJECT_BIN) { val_str = (char *) val.via.bin.ptr; val_str_size = val.via.bin.size; val_buf = flb_sds_create_len(val_str, val_str_size); if (val_buf == NULL) { flb_plg_error(ctx->ins, "val_buf creation is failed"); ret = -3; goto cleanup; } } else if (val.type == MSGPACK_OBJECT_STR) { val_str = (char *) val.via.str.ptr; val_str_size = val.via.str.size; val_buf = flb_sds_create_len(val_str, val_str_size); if (val_buf == NULL) { flb_plg_error(ctx->ins, "val_buf creation is failed"); ret = -3; goto cleanup; } } if (val_str != NULL && strstr(val_buf, "%") != NULL) { use_like = FLB_TRUE; flb_sds_destroy(val_buf); } if (use_like == FLB_TRUE) { flb_sds_cat_safe(clause, "NOT ", 4); } flb_sds_cat_safe(clause, key_str, key_str_size); if (use_like == FLB_TRUE) { flb_sds_cat_safe(clause, " LIKE ", 6); } else { flb_sds_cat_safe(clause, "!=", 2); } flb_sds_cat_safe(clause, "'", 1); flb_sds_cat_safe(clause, val_str, val_str_size); flb_sds_cat_safe(clause, "'", 1); idx++; } flb_sds_cat_safe(clause, ")", 1); } msgpack_unpacked_destroy(&result); return 0; cleanup: msgpack_unpacked_destroy(&result); return ret; } static int construct_where_clause(struct flb_we *ctx) { int ret; flb_sds_t clause; clause = flb_sds_create_size(256); if (!clause) { return -1; } if (ctx->service_include_buffer != NULL && ctx->service_include_buffer_size > 0) { ret = construct_include_clause(ctx, &clause); if (ret != 0) { goto cleanup; } } if (ctx->service_exclude_buffer != NULL && ctx->service_exclude_buffer_size > 0) { if (flb_sds_len(clause) > 0) { flb_sds_cat_safe(&clause, " AND ", 5); } ret = construct_exclude_clause(ctx, &clause); if (ret != 0) { goto cleanup; } } if (ctx->raw_where_clause != NULL){ if (flb_sds_len(clause) > 0) { flb_sds_cat_safe(&clause, " AND (", 6); flb_sds_cat_safe(&clause, ctx->raw_where_clause, strlen(ctx->raw_where_clause)); flb_sds_cat_safe(&clause, ")", 1); } else { flb_sds_cat_safe(&clause, ctx->raw_where_clause, strlen(ctx->raw_where_clause)); } } if (flb_sds_len(clause) > 0) { ctx->wmi_service->info->where_clause = clause; } return 0; cleanup: flb_sds_destroy(clause); return ret; } int we_wmi_service_init(struct flb_we *ctx) { int ret; struct cmt_gauge *g; ctx->wmi_service = flb_calloc(1, sizeof(struct we_wmi_service_counters)); if (!ctx->wmi_service) { flb_errno(); return -1; } ctx->wmi_service->operational = FLB_FALSE; g = cmt_gauge_create(ctx->cmt, "windows", "service", "info", "A metric for Windows Service information", 4, (char *[]) {"name", "display_name", "process_id", "run_as"}); if (!g) { return -1; } ctx->wmi_service->information = g; g = cmt_gauge_create(ctx->cmt, "windows", "service", "state", "A state of the service", 2, (char *[]){"name", "state"}); if (!g) { return -1; } ctx->wmi_service->state = g; g = cmt_gauge_create(ctx->cmt, "windows", "service", "start_mode", "A start mode of the service", 2, (char *[]){"name", "start_mode"}); if (!g) { return -1; } ctx->wmi_service->start_mode = g; g = cmt_gauge_create(ctx->cmt, "windows", "service", "status", "A status of the service", 2, (char *[]){"name", "status"}); if (!g) { return -1; } ctx->wmi_service->status = g; ctx->wmi_service->info = flb_calloc(1, sizeof(struct wmi_query_spec)); if (!ctx->wmi_service->info) { flb_errno(); return -1; } ctx->wmi_service->info->metric_instance = (void *)g; ctx->wmi_service->info->type = CMT_GAUGE; ctx->wmi_service->info->value_adjuster = nop_adjust; ctx->wmi_service->info->wmi_counter = "Win32_Service"; ctx->wmi_service->info->wmi_property = ""; ctx->wmi_service->info->label_property_count = 0; ctx->wmi_service->info->label_property_keys = NULL; ctx->wmi_service->info->where_clause = NULL; ret = construct_where_clause(ctx); if (ret != 0) { return ret; } ctx->wmi_service->operational = FLB_TRUE; return 0; } int we_wmi_service_exit(struct flb_we *ctx) { ctx->wmi_service->operational = FLB_FALSE; if (ctx->wmi_service->info->where_clause != NULL) { flb_sds_destroy(ctx->wmi_service->info->where_clause); } flb_free(ctx->wmi_service->info); flb_free(ctx->wmi_service); return 0; } int we_wmi_service_update(struct flb_we *ctx) { IEnumWbemClassObject* enumerator = NULL; HRESULT hr; IWbemClassObject *class_obj = NULL; ULONG ret = 0; int i = 0; uint64_t timestamp = 0; char *service_name = NULL; char *display_name = NULL; char *pid = NULL; char *run_as = NULL; char *str_prop = NULL; char *state = NULL; char *start_mode = NULL; char *status = NULL; char **states = (char *[]){ "stopped", "start pending", "stop pending", "running", "continue pending", "pause pending", "paused", "unknown", NULL }; char **statuses = (char *[]){ "ok", "error", "degraded", "unknown", "pred fail", "starting", "stopping", "service", "stressed", "nonrecover", "no contact", "lost comm", NULL }; char **start_modes = (char *[]) { "boot", "system", "auto", "manual", "disabled", NULL }; if (!ctx->wmi_service->operational) { flb_plg_error(ctx->ins, "windows_service collector not yet in operational state"); return -1; } if (FAILED(we_wmi_coinitialize(ctx))) { return -1; } timestamp = cfl_time_now(); if (FAILED(we_wmi_execute_query(ctx, ctx->wmi_service->info, &enumerator))) { return -1; } while (enumerator) { hr = enumerator->lpVtbl->Next(enumerator, WBEM_INFINITE, 1, &class_obj, &ret); if (0 == ret) { break; } service_name = we_wmi_get_property_str_value(ctx, "Name", class_obj); display_name = we_wmi_get_property_str_value(ctx, "DisplayName", class_obj); pid = we_wmi_get_property_str_value(ctx, "ProcessID", class_obj); run_as = we_wmi_get_property_str_value(ctx, "StartName", class_obj); state = we_wmi_get_property_str_value(ctx, "State", class_obj); start_mode = we_wmi_get_property_str_value(ctx, "StartMode", class_obj); status = we_wmi_get_property_str_value(ctx, "Status", class_obj); /* Information */ cmt_gauge_set(ctx->wmi_service->information, timestamp, 1.0, 4, (char *[]){ service_name, display_name, pid, run_as}); /* State */ for (i = 0; states[i] != NULL; i++) { if (strcasecmp(state, states[i]) == 0) { cmt_gauge_set(ctx->wmi_service->state, timestamp, 1.0, 2, (char *[]){ service_name, states[i]}); } else { cmt_gauge_set(ctx->wmi_service->state, timestamp, 0.0, 2, (char *[]){ service_name, states[i]}); } } /* Start Mode */ for (i = 0; start_modes[i] != NULL; i++) { if (strcasecmp(start_mode, start_modes[i]) == 0) { cmt_gauge_set(ctx->wmi_service->start_mode, timestamp, 1.0, 2, (char *[]){ service_name, start_modes[i]}); } else { cmt_gauge_set(ctx->wmi_service->start_mode, timestamp, 0.0, 2, (char *[]){ service_name, start_modes[i]}); } } /* Status */ for (i = 0; statuses[i] != NULL; i++) { if (strcasecmp(status, statuses[i]) == 0) { cmt_gauge_set(ctx->wmi_service->status, timestamp, 1.0, 2, (char *[]){ service_name, statuses[i]}); } else { cmt_gauge_set(ctx->wmi_service->status, timestamp, 0.0, 2, (char *[]){ service_name, statuses[i]}); } } class_obj->lpVtbl->Release(class_obj); flb_free(service_name); flb_free(display_name); flb_free(pid); flb_free(run_as); flb_free(state); flb_free(start_mode); flb_free(status); } enumerator->lpVtbl->Release(enumerator); we_wmi_cleanup(ctx); return 0; }