1/*
2    Copyright (c) 2014 Intel Corporation.  All Rights Reserved.
3
4    Redistribution and use in source and binary forms, with or without
5    modification, are permitted provided that the following conditions
6    are met:
7
8      * Redistributions of source code must retain the above copyright
9        notice, this list of conditions and the following disclaimer.
10      * Redistributions in binary form must reproduce the above copyright
11        notice, this list of conditions and the following disclaimer in the
12        documentation and/or other materials provided with the distribution.
13      * Neither the name of Intel Corporation nor the names of its
14        contributors may be used to endorse or promote products derived
15        from this software without specific prior written permission.
16
17    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*/
29
30
31#include "offload_env.h"
32#include <string.h>
33#include <ctype.h>
34#include "offload_util.h"
35#include "liboffload_error_codes.h"
36
37// for environment variables valid on all cards
38const int MicEnvVar::any_card = -1;
39
40MicEnvVar::~MicEnvVar()
41{
42    for (std::list<MicEnvVar::CardEnvVars*>::const_iterator
43         it = card_spec_list.begin();
44         it != card_spec_list.end(); it++) {
45        CardEnvVars *card_data = *it;
46        delete card_data;
47    }
48}
49
50MicEnvVar::VarValue::~VarValue()
51{
52    free(env_var_value);
53}
54
55MicEnvVar::CardEnvVars::~CardEnvVars()
56{
57    for (std::list<MicEnvVar::VarValue*>::const_iterator it = env_vars.begin();
58        it != env_vars.end(); it++) {
59            VarValue *var_value = *it;
60            delete var_value;
61    }
62}
63
64// Searching for card in "card_spec_list" list with the same "number"
65
66MicEnvVar::CardEnvVars* MicEnvVar::get_card(int number)
67{
68    if (number == any_card) {
69        return &common_vars;
70    }
71    for (std::list<MicEnvVar::CardEnvVars*>::const_iterator
72         it = card_spec_list.begin();
73         it != card_spec_list.end(); it++) {
74        CardEnvVars *card_data = *it;
75        if (card_data->card_number == number) {
76            return card_data;
77        }
78    }
79    return NULL;
80}
81
82// Searching for environment variable in "env_var" list with the same name
83
84MicEnvVar::VarValue* MicEnvVar::CardEnvVars::find_var(
85    char* env_var_name,
86    int env_var_name_length
87)
88{
89    for (std::list<MicEnvVar::VarValue*>::const_iterator it = env_vars.begin();
90        it != env_vars.end(); it++) {
91            VarValue *var_value = *it;
92            if (var_value->length == env_var_name_length &&
93                !strncmp(var_value->env_var, env_var_name,
94                         env_var_name_length)) {
95                return var_value;
96            }
97    }
98    return NULL;
99}
100
101void MicEnvVar::analyze_env_var(char *env_var_string)
102{
103    char          *env_var_name;
104    char          *env_var_def;
105    int           card_number;
106    int           env_var_name_length;
107    MicEnvVarKind env_var_kind;
108
109    env_var_kind = get_env_var_kind(env_var_string,
110                                    &card_number,
111                                    &env_var_name,
112                                    &env_var_name_length,
113                                    &env_var_def);
114    switch (env_var_kind) {
115        case c_mic_var:
116        case c_mic_card_var:
117            add_env_var(card_number,
118                        env_var_name,
119                        env_var_name_length,
120                        env_var_def);
121            break;
122        case c_mic_card_env:
123            mic_parse_env_var_list(card_number, env_var_def);
124            break;
125        case c_no_mic:
126        default:
127            break;
128    }
129}
130
131void MicEnvVar::add_env_var(
132    int card_number,
133    char *env_var_name,
134    int env_var_name_length,
135    char *env_var_def
136)
137{
138    VarValue *var;
139    CardEnvVars *card;
140
141    // The case corresponds to common env var definition of kind
142    // <mic-prefix>_<var>
143    if (card_number == any_card) {
144        card = &common_vars;
145    }
146    else {
147        card = get_card(card_number);
148        if (!card) {
149            // definition for new card occured
150            card = new CardEnvVars(card_number);
151            card_spec_list.push_back(card);
152        }
153
154    }
155    var = card->find_var(env_var_name, env_var_name_length);
156    if (!var) {
157        // put new env var definition in "env_var" list
158        var = new VarValue(env_var_name, env_var_name_length, env_var_def);
159        card->env_vars.push_back(var);
160    }
161}
162
163// The routine analyses string pointed by "env_var_string" argument
164// according to the following syntax:
165//
166// Specification of prefix for MIC environment variables
167// MIC_ENV_PREFIX=<mic-prefix>
168//
169// Setting single MIC environment variable
170// <mic-prefix>_<var>=<value>
171// <mic-prefix>_<card-number>_<var>=<value>
172
173// Setting multiple MIC environment variables
174// <mic-prefix>_<card-number>_ENV=<env-vars>
175
176MicEnvVarKind MicEnvVar::get_env_var_kind(
177    char *env_var_string,
178    int *card_number,
179    char **env_var_name,
180    int *env_var_name_length,
181    char **env_var_def
182)
183{
184    int len = strlen(prefix);
185    char *c = env_var_string;
186    int num = 0;
187    bool card_is_set = false;
188
189    if (strncmp(c, prefix, len) != 0 || c[len] != '_') {
190            return c_no_mic;
191    }
192    c += len + 1;
193
194    *card_number = any_card;
195    if (isdigit(*c)) {
196        while (isdigit (*c)) {
197            num = (*c++ - '0') + (num * 10);
198        }
199    if (*c != '_') {
200        return c_no_mic;
201    }
202    c++;
203        *card_number = num;
204        card_is_set = true;
205    }
206    if (!isalpha(*c)) {
207        return c_no_mic;
208    }
209    *env_var_name = *env_var_def = c;
210    if (strncmp(c, "ENV=", 4) == 0) {
211        if (!card_is_set) {
212            *env_var_name_length = 3;
213            *env_var_name = *env_var_def = c;
214            *env_var_def = strdup(*env_var_def);
215            return  c_mic_var;
216        }
217        *env_var_def = c + strlen("ENV=");
218        *env_var_def = strdup(*env_var_def);
219        return c_mic_card_env;
220    }
221    if (isalpha(*c)) {
222        *env_var_name_length = 0;
223        while (isalnum(*c) || *c == '_') {
224            c++;
225            (*env_var_name_length)++;
226        }
227    }
228    if (*c != '=') {
229        return c_no_mic;
230    }
231    *env_var_def = strdup(*env_var_def);
232    return card_is_set? c_mic_card_var : c_mic_var;
233}
234
235// analysing <env-vars> in form:
236// <mic-prefix>_<card-number>_ENV=<env-vars>
237// where:
238//
239// <env-vars>:
240//                <env-var>
241//                <env-vars> | <env-var>
242//
243// <env-var>:
244//                variable=value
245//                variable="value"
246//                variable=
247
248void MicEnvVar::mic_parse_env_var_list(
249    int card_number, char *env_vars_def_list)
250{
251    char *c = env_vars_def_list;
252    char *env_var_name;
253    int  env_var_name_length;
254    char *env_var_def;
255    bool var_is_quoted;
256
257    if (*c == '"') {
258        c++;
259    }
260    while (*c != 0) {
261        var_is_quoted = false;
262        env_var_name = c;
263        env_var_name_length = 0;
264        if (isalpha(*c)) {
265            while (isalnum(*c) || *c == '_') {
266                c++;
267                env_var_name_length++;
268            }
269        }
270        else {
271            LIBOFFLOAD_ERROR(c_mic_parse_env_var_list1);
272            return;
273        }
274        if (*c != '=') {
275            LIBOFFLOAD_ERROR(c_mic_parse_env_var_list2);
276            return;
277        }
278        c++;
279
280        if (*c == '"') {
281            var_is_quoted = true;
282            c++;
283        }
284        // Environment variable values that contain | will need to be escaped.
285        while (*c != 0 && *c != '|' &&
286               (!var_is_quoted || *c != '"'))
287        {
288            // skip escaped symbol
289            if (*c == '\\') {
290                c++;
291            }
292            c++;
293        }
294        if (var_is_quoted) {
295            c++; // for "
296            while (*c != 0 && *c != '|') {
297                c++;
298            }
299        }
300
301        int sz = c - env_var_name;
302        env_var_def = (char*)malloc(sz);
303        if (env_var_def == NULL)
304          LIBOFFLOAD_ERROR(c_malloc);
305        memcpy(env_var_def, env_var_name, sz);
306        env_var_def[sz] = 0;
307
308        if (*c == '|') {
309            c++;
310            while (*c != 0 && *c == ' ') {
311                c++;
312            }
313        }
314        add_env_var(card_number,
315                    env_var_name,
316                    env_var_name_length,
317                    env_var_def);
318    }
319}
320
321// Collect all definitions for the card with number "card_num".
322// The returned result is vector of string pointers defining one
323// environment variable. The vector is terminated by NULL pointer.
324// In the begining of the vector there are env vars defined as
325// <mic-prefix>_<card-number>_<var>=<value>
326// or
327// <mic-prefix>_<card-number>_ENV=<env-vars>
328// where <card-number> is equal to "card_num"
329// They are followed by definitions valid for any card
330// and absent in previous definitions.
331
332char** MicEnvVar::create_environ_for_card(int card_num)
333{
334    VarValue *var_value;
335    VarValue *var_value_find;
336    CardEnvVars *card_data = get_card(card_num);
337    CardEnvVars *card_data_common;
338    std::list<char*> new_env;
339    char **rez;
340
341    if (!prefix) {
342        return NULL;
343    }
344    // There is no personel env var definitions for the card with
345    // number "card_num"
346    if (!card_data) {
347        return create_environ_for_card(any_card);
348    }
349
350    for (std::list<MicEnvVar::VarValue*>::const_iterator
351         it = card_data->env_vars.begin();
352         it != card_data->env_vars.end(); it++) {
353        var_value = *it;
354        new_env.push_back(var_value->env_var_value);
355    }
356
357    if (card_num != any_card) {
358        card_data_common = get_card(any_card);
359        for (std::list<MicEnvVar::VarValue*>::const_iterator
360             it = card_data_common->env_vars.begin();
361             it != card_data_common->env_vars.end(); it++) {
362            var_value = *it;
363            var_value_find = card_data->find_var(var_value->env_var,
364                                                 var_value->length);
365            if (!var_value_find) {
366                new_env.push_back(var_value->env_var_value);
367            }
368        }
369    }
370
371    int new_env_size = new_env.size();
372    rez = (char**) malloc((new_env_size + 1) * sizeof(char*));
373    if (rez == NULL)
374      LIBOFFLOAD_ERROR(c_malloc);
375    std::copy(new_env.begin(), new_env.end(), rez);
376    rez[new_env_size] = 0;
377    return rez;
378}
379