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