@@ -83,78 +83,95 @@ valid_env_var_name(const gchar *name)
8383 return * name == '\0' ;
8484}
8585
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+ */
8696static void
87- load_env_var_line (char * line )
97+ load_env_var_line (const char * line )
8898{
99+ gchar * stripped = NULL ;
89100 gchar * name = NULL ;
90101 gchar * value = NULL ;
91- gchar * end = NULL ;
92- gchar * comment = NULL ;
93102
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 ;
96109
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
99113 goto done ;
100114 }
101115
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+ }
123121
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 ;
129125 }
130126
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.
135148 */
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 ;
141152 }
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" ;
148155 goto done ;
149156 }
150157
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+ */
152162 // coverity[tainted_string] Can't easily be changed right now
153- setenv (name , value , 0 );
163+ setenv (name , (( argc == 1 )? argv [ 0 ] : "" ) , 0 );
154164
155165done :
166+ if (reason != NULL ) {
167+ crm_warn ("Failed to perform environment variable assignment '%s': %s" ,
168+ line , reason );
169+ }
170+ g_free (stripped );
156171 g_free (name );
157172 g_free (value );
173+ g_strfreev (argv );
174+ g_clear_error (& error );
158175}
159176
160177#define CONTAINER_ENV_FILE "/etc/pacemaker/pcmk-init.env"
0 commit comments