@@ -93,103 +93,118 @@ find_env_var_name(char *line, char **first, char **last)
9393 return FALSE;
9494}
9595
96+ #define CONTAINER_ENV_FILE "/etc/pacemaker/pcmk-init.env"
97+
9698static void
97- load_env_vars (const char * filename )
99+ load_env_vars (void )
98100{
99101 /* We haven't forked or initialized logging yet, so don't leave any file
100102 * descriptors open, and don't log -- silently ignore errors.
101103 */
102- FILE * fp = fopen (filename , "r" );
103- char line [LINE_MAX ] = { 0 , };
104+ FILE * fp = fopen (CONTAINER_ENV_FILE , "r" );
105+ char * line = NULL ;
106+ size_t buf_size = 0 ;
104107
105108 if (fp == NULL ) {
106109 return ;
107110 }
108111
109- while (fgets ( line , LINE_MAX , fp ) != NULL ) {
112+ while (getline ( & line , & buf_size , fp ) != -1 ) {
110113 char * name = NULL ;
111114 char * end = NULL ;
112- char * value = NULL ;
113115 char * quote = NULL ;
116+ char * value = NULL ;
117+ char * value_end = NULL ;
114118
115119 // Look for valid name immediately followed by equals sign
116- if (find_env_var_name (line , & name , & end ) && (* ++ end == '=' )) {
120+ if (!find_env_var_name (line , & name , & end ) || (* ++ end != '=' )) {
121+ continue ;
122+ }
117123
118- // Null-terminate name, and advance beyond equals sign
119- * end ++ = '\0' ;
124+ // Null-terminate name, and advance beyond equals sign
125+ * end ++ = '\0' ;
126+
127+ // Check whether value is quoted
128+ if ((* end == '\'' ) || (* end == '"' )) {
129+ quote = end ++ ;
130+ }
120131
121- // Check whether value is quoted
122- if ((* end == '\'' ) || (* end == '"' )) {
123- quote = end ++ ;
132+ // Set beginning of value
133+ value = end ;
134+
135+ // Find end of value
136+ if (quote != NULL ) {
137+ /* Value is remaining characters up to next non-backslashed matching
138+ * quote character
139+ *
140+ * @TODO This breaks when the value ends with a literal escaped
141+ * backslash. For example: VAR='value\\'.
142+ */
143+ while (((* end != * quote ) || (* (end - 1 ) == '\\' ))
144+ && (* end != '\0' )) {
145+ end ++ ;
124146 }
125- value = end ;
126-
127- if (quote != NULL ) {
128- /* Value is remaining characters up to next non-backslashed
129- * matching quote character.
130- */
131- while (((* end != * quote ) || (* (end - 1 ) == '\\' ))
132- && (* end != '\0' )) {
133- end ++ ;
134- }
135- if (* end == * quote ) {
136- // Null-terminate value, and advance beyond close quote
137- * end ++ = '\0' ;
138- } else {
139- // Matching closing quote wasn't found
140- value = NULL ;
141- }
142-
143- } else {
144- /* Value is remaining characters up to next non-backslashed
145- * whitespace.
146- */
147- while ((!isspace (* end ) || (* (end - 1 ) == '\\' ))
148- && (* end != '\0' )) {
149- end ++ ;
150- }
151-
152- if (end == (line + LINE_MAX - 1 )) {
153- // Line was too long
154- value = NULL ;
155- }
156- // Do NOT null-terminate value (yet)
147+
148+ if (* end != * quote ) {
149+ // Matching closing quote wasn't found
150+ continue ;
157151 }
158152
159- /* We have a valid name and value, and end is now the character
160- * after the closing quote or the first whitespace after the
161- * unquoted value. Make sure the rest of the line is just whitespace
162- * or a comment.
153+ /* Null-terminate value at the closing quote and advance the end
154+ * pointer beyond it.
155+ */
156+ * end ++ = '\0' ;
157+
158+ } else {
159+ /* Value is remaining characters up to next non-backslashed
160+ * whitespace
161+ *
162+ * @TODO This breaks when the value ends with a literal escaped
163+ * backslash and is followed by a space.
163164 */
164- if (value != NULL ) {
165- char * value_end = end ;
166-
167- while (isspace (* end ) && (* end != '\n' )) {
168- end ++ ;
169- }
170- if ((* end == '\n' ) || (* end == '#' )) {
171- if (quote == NULL ) {
172- // Now we can null-terminate an unquoted value
173- * value_end = '\0' ;
174- }
175-
176- // Don't overwrite (bundle options take precedence)
177- setenv (name , value , 0 );
178-
179- } else {
180- value = NULL ;
181- }
165+ while ((!isspace (* end ) || (* (end - 1 ) == '\\' ))
166+ && (* end != '\0' )) {
167+ end ++ ;
182168 }
183- }
184169
185- if ((value == NULL ) && (strchr (line , '\n' ) == NULL )) {
186- // Eat remainder of line beyond LINE_MAX
187- if (fscanf (fp , "%*[^\n]\n" ) == EOF ) {
188- value = NULL ; // Don't care, make compiler happy
170+ if (* end == '\0' ) {
171+ // Malformed line (contains null terminator before newline)
172+ continue ;
189173 }
190174 }
175+
176+ /* We have a valid name and value, and end is now the character after
177+ * the closing quote or the first whitespace after the unquoted value.
178+ * Make sure the rest of the line is just whitespace or a comment.
179+ */
180+ value_end = end ;
181+
182+ while (isspace (* end ) && (* end != '\n' )) {
183+ end ++ ;
184+ }
185+
186+ if ((* end != '\n' ) && (* end != '#' )) {
187+ // Found garbage after value
188+ continue ;
189+ }
190+
191+ // Null-terminate value (it's safe to do it again for a quoted value)
192+ * value_end = '\0' ;
193+
194+ // Don't overwrite (bundle options take precedence)
195+ setenv (name , value , 0 );
196+ }
197+
198+ // getline() returns -1 on EOF or error
199+ if (errno != 0 ) {
200+ int rc = errno ;
201+
202+ crm_err ("Error while reading environment variables from "
203+ CONTAINER_ENV_FILE ": %s" ,
204+ pcmk_rc_str (rc ));
191205 }
192206 fclose (fp );
207+ free (line );
193208}
194209
195210void
@@ -221,7 +236,7 @@ remoted_spawn_pidone(int argc, char **argv, char **envp)
221236 * To allow for that, look for a special file containing a shell-like syntax
222237 * of name/value pairs, and export those into the environment.
223238 */
224- load_env_vars ("/etc/pacemaker/pcmk-init.env" );
239+ load_env_vars ();
225240
226241 if (strcmp (pid1 , "vars" ) == 0 ) {
227242 return ;
0 commit comments