1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3 /* Fluent Bit
4 * ==========
5 * Copyright (C) 2019-2021 The Fluent Bit Authors
6 * Copyright (C) 2015-2018 Treasure Data Inc.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 #include <fluent-bit/flb_info.h>
22 #include <fluent-bit/flb_hash.h>
23 #include <fluent-bit/flb_mem.h>
24 #include <fluent-bit/flb_log.h>
25 #include <fluent-bit/flb_str.h>
26 #include <fluent-bit/flb_env.h>
27
28 #include <stdlib.h>
29
buf_append(flb_sds_t buf,const char * str,int len)30 static inline flb_sds_t buf_append(flb_sds_t buf, const char *str, int len)
31 {
32 flb_sds_t tmp;
33
34 tmp = flb_sds_cat(buf, str, len);
35 if (!tmp) {
36 return NULL;
37 }
38
39 return tmp;
40 }
41
42 /* Preset some useful variables */
env_preset(struct flb_env * env)43 static int env_preset(struct flb_env *env)
44 {
45 int ret;
46 char *buf;
47 char tmp[512];
48
49 /*
50 * ${HOSTNAME} this variable is very useful to identify records,
51 * despite this variable is recognized by the Shell, that does not
52 * means that is exposed as a real environment variable, e.g:
53 *
54 * 1. $ echo $HOSTNAME
55 * monotop
56 * 2. $ env | grep HOSTNAME
57 * (nothing)
58 */
59 buf = getenv("HOSTNAME");
60 if (!buf) {
61 ret = gethostname(tmp, sizeof(tmp) - 1);
62 if (ret == 0) {
63 flb_env_set(env, "HOSTNAME", tmp);
64 }
65 }
66
67 return 0;
68 }
69
flb_env_create()70 struct flb_env *flb_env_create()
71 {
72 struct flb_env *env;
73 struct flb_hash *ht;
74
75 env = flb_malloc(sizeof(struct flb_env));
76 if (!env) {
77 flb_errno();
78 return NULL;
79 }
80
81 /* Create the hash-table */
82 ht = flb_hash_create(FLB_HASH_EVICT_NONE, FLB_ENV_SIZE, -1);
83 if (!ht) {
84 flb_free(env);
85 return NULL;
86 }
87
88 env->warn_unused = FLB_TRUE;
89 env->ht = ht;
90 env_preset(env);
91
92 return env;
93 }
94
flb_env_destroy(struct flb_env * env)95 void flb_env_destroy(struct flb_env *env)
96 {
97 flb_hash_destroy(env->ht);
98 flb_free(env);
99 }
100
flb_env_set(struct flb_env * env,const char * key,const char * val)101 int flb_env_set(struct flb_env *env, const char *key, const char *val)
102 {
103 int id;
104 int klen;
105 int vlen;
106 void *out_buf;
107 size_t out_size;
108
109 /* Get lengths */
110 klen = strlen(key);
111 vlen = strlen(val);
112
113 /* Check if the key is already set */
114 id = flb_hash_get(env->ht, key, klen, &out_buf, &out_size);
115 if (id >= 0) {
116 /* Remove the old entry */
117 flb_hash_del(env->ht, key);
118 }
119
120 /* Register the new key */
121 id = flb_hash_add(env->ht, key, klen, (void *) val, vlen);
122 return id;
123 }
124
flb_env_get(struct flb_env * env,const char * key)125 const char *flb_env_get(struct flb_env *env, const char *key)
126 {
127 int len;
128 int ret;
129 void *out_buf;
130 size_t out_size;
131
132 if (!key) {
133 return NULL;
134 }
135
136 len = strlen(key);
137
138 /* Try to get the value from the hash table */
139 ret = flb_hash_get(env->ht, key, len, &out_buf, &out_size);
140 if (ret >= 0) {
141 return (char *) out_buf;
142 }
143
144 /* If it was not found, try to get it from the real environment */
145 out_buf = getenv(key);
146 if (!out_buf) {
147 return NULL;
148 }
149
150 if (strlen(out_buf) == 0) {
151 return NULL;
152 }
153
154 return (char *) out_buf;
155 }
156
157 /*
158 * Given a 'value', lookup for variables, if found, return a new composed
159 * sds string.
160 */
flb_env_var_translate(struct flb_env * env,const char * value)161 flb_sds_t flb_env_var_translate(struct flb_env *env, const char *value)
162 {
163 int i;
164 int len;
165 int v_len;
166 int e_len;
167 int pre_var;
168 int have_var = FLB_FALSE;
169 const char *env_var = NULL;
170 char *v_start = NULL;
171 char *v_end = NULL;
172 char tmp[64];
173 flb_sds_t buf;
174 flb_sds_t s;
175
176 if (!value) {
177 return NULL;
178 }
179
180 len = strlen(value);
181 buf = flb_sds_create_size(len);
182 if (!buf) {
183 return NULL;
184 }
185
186 for (i = 0; i < len; i++) {
187 v_start = strstr(value + i, "${");
188 if (!v_start) {
189 break;
190 }
191
192 v_end = strstr(value + i, "}");
193 if (!v_end) {
194 break;
195 }
196
197 v_start += 2;
198 v_len = v_end - v_start;
199 if (v_len <= 0) {
200 break;
201 }
202
203 /* variable */
204 strncpy(tmp, v_start, v_len);
205 tmp[v_len] = '\0';
206 have_var = FLB_TRUE;
207
208 /* Append pre-variable content */
209 pre_var = (v_start - 2) - (value + i);
210 if (pre_var > 0) {
211 s = buf_append(buf, value + i, (v_start - 2) - (value + i));
212 if (!s) {
213 flb_sds_destroy(buf);
214 return NULL;
215 }
216 if (s != buf) {
217 buf = s;
218 }
219 }
220
221 /* Lookup the variable in our env-hash */
222 env_var = flb_env_get(env, tmp);
223 if (env_var) {
224 e_len = strlen(env_var);
225 s = buf_append(buf, env_var, e_len);
226 if (!s) {
227 flb_sds_destroy(buf);
228 return NULL;
229 }
230 if (s != buf) {
231 buf = s;
232 }
233 }
234 else if (env->warn_unused == FLB_TRUE) {
235 flb_warn("[env] variable ${%s} is used but not set", tmp);
236 }
237 i += (v_start - (value + i)) + v_len;
238 }
239
240 /* Copy the remaining value into our buffer */
241 if (v_end) {
242 if (have_var == FLB_TRUE && (value + len) - (v_end + 1) > 0) {
243 s = buf_append(buf, v_end + 1, (value + len) - (v_end + 1));
244 if (!s) {
245 flb_sds_destroy(buf);
246 return NULL;
247 }
248 if (s != buf) {
249 buf = s;
250 }
251 }
252 }
253
254 if (flb_sds_len(buf) == 0) {
255 /*
256 * If the output length buffer is zero, it could mean:
257 *
258 * - just one variable was given and it don't have any value
259 * - no variables given (keep original value)
260 *
261 * In order to avoid problems in the caller, if a variable is null
262 * and is the only one content available, return a new empty memory
263 * string.
264 */
265 if (have_var == FLB_TRUE) {
266 return flb_sds_copy(buf, "", 0);
267 }
268 else {
269 return flb_sds_copy(buf, value, len);
270 }
271 }
272
273 return buf;
274 }
275