@@ -83,78 +83,95 @@ valid_env_var_name(const gchar *name)
83
83
return * name == '\0' ;
84
84
}
85
85
86
+ /*!
87
+ * \internal
88
+ * \brief Read one environment variable assignment and set the value
89
+ *
90
+ * The assignment must be contained within a single line. A trailing comment is
91
+ * ignored. This function handles backslashes, single quotes, and double quotes
92
+ * in a manner similar to a POSIX shell.
93
+ *
94
+ * \param[in] line Line containing an environment variable assignment statement
95
+ */
86
96
static void
87
- load_env_var_line (char * line )
97
+ load_env_var_line (const char * line )
88
98
{
99
+ gchar * stripped = NULL ;
89
100
gchar * name = NULL ;
90
101
gchar * value = NULL ;
91
- gchar * end = NULL ;
92
- gchar * comment = NULL ;
93
102
94
- // Strip leading and trailing whitespace
95
- g_strstrip (line );
103
+ int rc = pcmk_rc_ok ;
104
+ const char * reason = NULL ;
105
+
106
+ gint argc = 0 ;
107
+ gchar * * argv = NULL ;
108
+ GError * error = NULL ;
96
109
97
- if ((pcmk__scan_nvpair (line , & name , & value ) != pcmk_rc_ok )
98
- || !valid_env_var_name (name )) {
110
+ stripped = g_strstrip (g_strdup (line ));
111
+ if (pcmk__str_empty (stripped ) || (stripped [0 ] == '#' )) {
112
+ // Empty line, nothing to do
99
113
goto done ;
100
114
}
101
115
102
- if ((* value == '\'' ) || (* value == '"' )) {
103
- const char quote = * value ;
104
-
105
- // Strip the leading quote
106
- * value = ' ' ;
107
- g_strchug (value );
108
-
109
- /* Value is remaining characters up to next non-backslashed matching
110
- * quote character.
111
- */
112
- for (end = value ;
113
- (* end != '\0' ) && ((* end != quote ) || (* (end - 1 ) == '\\' ));
114
- end ++ );
115
-
116
- if (* end != quote ) {
117
- // Matching closing quote wasn't found
118
- goto done ;
119
- }
120
-
121
- // Discard closing quote and advance to check for trailing garbage
122
- * end ++ = '\0' ;
116
+ rc = pcmk__scan_nvpair (stripped , & name , & value );
117
+ if (rc != pcmk_rc_ok ) {
118
+ reason = pcmk_rc_str (rc );
119
+ goto done ;
120
+ }
123
121
124
- } else {
125
- // Value is remaining characters up to next non-backslashed whitespace
126
- for (end = value ;
127
- (* end != '\0' ) && (!isspace (* end ) || (* (end - 1 ) == '\\' ));
128
- end ++ );
122
+ if (!valid_env_var_name (name )) {
123
+ reason = "invalid environment variable name" ;
124
+ goto done ;
129
125
}
130
126
131
- /* We have a valid name and value, and end is now the character after the
132
- * closing quote or the first whitespace after the unquoted value. Make sure
133
- * the rest of the line, if any, is just optional whitespace followed by a
134
- * comment.
127
+ /* g_shell_parse_argv() does the following in a manner similar to the shell:
128
+ * * tokenizes the value
129
+ * * strips a trailing '#' comment if one exists
130
+ * * handles backslashes, single quotes, and double quotes
131
+ *
132
+ * The value is valid only if it consists zero or one token after stripping
133
+ * the comment (that is, if argc is 0 or 1) and does not have mismatched
134
+ * quotes.
135
+ *
136
+ * For example, the following line is valid, and argc will be set to 1.
137
+ * (Note that value is everything after "VAR=".)
138
+ *
139
+ * VAR=' some value "with quotes"' # comment
140
+ *
141
+ * We will set VAR to " some value \"with quotes\"".
142
+ *
143
+ * The following line is invalid, and argc will be set to 2.
144
+ *
145
+ * VAR=' some value "with quotes"' more # comment
146
+ *
147
+ * In this case, we will not set a value for VAR.
135
148
*/
136
-
137
- // Strip trailing comment beginning with '#'
138
- comment = strchr (end , '#' );
139
- if (comment != NULL ) {
140
- * comment = '\0' ;
149
+ if (!g_shell_parse_argv (value , & argc , & argv , & error )) {
150
+ reason = error -> message ;
151
+ goto done ;
141
152
}
142
-
143
- // Strip any remaining trailing whitespace from value
144
- g_strchomp (end );
145
-
146
- if (* end != '\0' ) {
147
- // Found garbage after value
153
+ if (argc > 1 ) {
154
+ reason = "garbage after value" ;
148
155
goto done ;
149
156
}
150
157
151
- // Don't overwrite (bundle options take precedence)
158
+ /* Don't overwrite (bundle options take precedence). argv[0] contains the
159
+ * value after stripping the trailing comment and processing quotes and
160
+ * backslashes.
161
+ */
152
162
// coverity[tainted_string] Can't easily be changed right now
153
- setenv (name , value , 0 );
163
+ setenv (name , (( argc == 1 )? argv [ 0 ] : "" ) , 0 );
154
164
155
165
done :
166
+ if (reason != NULL ) {
167
+ crm_warn ("Failed to perform environment variable assignment '%s': %s" ,
168
+ line , reason );
169
+ }
170
+ g_free (stripped );
156
171
g_free (name );
157
172
g_free (value );
173
+ g_strfreev (argv );
174
+ g_clear_error (& error );
158
175
}
159
176
160
177
#define CONTAINER_ENV_FILE "/etc/pacemaker/pcmk-init.env"
0 commit comments