1 /*
2  * strutil.c
3  *
4  * Copyright (c) 2ndQuadrant, 2010-2020
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 
24 #include "repmgr.h"
25 #include "log.h"
26 #include "strutil.h"
27 
28 static int
29 xvsnprintf(char *str, size_t size, const char *format, va_list ap)
30 __attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 0)));
31 
32 static void
33 _key_value_list_set(KeyValueList *item_list, bool replace, const char *key, const char *value);
34 
35 static int
xvsnprintf(char * str,size_t size,const char * format,va_list ap)36 xvsnprintf(char *str, size_t size, const char *format, va_list ap)
37 {
38 	int			retval;
39 
40 	retval = vsnprintf(str, size, format, ap);
41 
42 	if (retval >= (int) size)
43 	{
44 		log_error(_("buffer of specified size not large enough to format entire string '%s'"),
45 				  str);
46 		exit(ERR_STR_OVERFLOW);
47 	}
48 
49 	return retval;
50 }
51 
52 
53 int
maxlen_snprintf(char * str,const char * format,...)54 maxlen_snprintf(char *str, const char *format,...)
55 {
56 	va_list		arglist;
57 	int			retval;
58 
59 	va_start(arglist, format);
60 	retval = xvsnprintf(str, MAXLEN, format, arglist);
61 	va_end(arglist);
62 
63 	return retval;
64 }
65 
66 
67 int
maxpath_snprintf(char * str,const char * format,...)68 maxpath_snprintf(char *str, const char *format,...)
69 {
70 	va_list		arglist;
71 	int			retval;
72 
73 	va_start(arglist, format);
74 	retval = xvsnprintf(str, MAXPGPATH, format, arglist);
75 	va_end(arglist);
76 
77 	return retval;
78 }
79 
80 
81 void
append_where_clause(PQExpBufferData * where_clause,const char * format,...)82 append_where_clause(PQExpBufferData *where_clause, const char *format,...)
83 {
84 	va_list		arglist;
85 	char		stringbuf[MAXLEN];
86 
87 	va_start(arglist, format);
88 	(void) xvsnprintf(stringbuf, MAXLEN, format, arglist);
89 	va_end(arglist);
90 
91 	if (where_clause->data[0] == '\0')
92 	{
93 		appendPQExpBufferStr(where_clause,
94 							 " WHERE ");
95 	}
96 	else
97 	{
98 		appendPQExpBufferStr(where_clause,
99 							 " AND ");
100 	}
101 
102 	appendPQExpBufferStr(where_clause,
103 						 stringbuf);
104 
105 }
106 
107 
108 void
item_list_append(ItemList * item_list,const char * message)109 item_list_append(ItemList *item_list, const char *message)
110 {
111 	item_list_append_format(item_list, "%s", message);
112 }
113 
114 
115 void
item_list_append_format(ItemList * item_list,const char * format,...)116 item_list_append_format(ItemList *item_list, const char *format,...)
117 {
118 	ItemListCell *cell;
119 	va_list		arglist;
120 
121 	if (item_list == NULL)
122 		return;
123 
124 	cell = (ItemListCell *) pg_malloc0(sizeof(ItemListCell));
125 
126 	if (cell == NULL)
127 	{
128 		log_error(_("unable to allocate memory; terminating."));
129 		exit(ERR_OUT_OF_MEMORY);
130 	}
131 
132 	cell->string = pg_malloc0(MAXLEN);
133 
134 	va_start(arglist, format);
135 
136 	(void) xvsnprintf(cell->string, MAXLEN, format, arglist);
137 	va_end(arglist);
138 
139 
140 	if (item_list->tail)
141 		item_list->tail->next = cell;
142 	else
143 		item_list->head = cell;
144 
145 	item_list->tail = cell;
146 }
147 
148 
149 void
item_list_free(ItemList * item_list)150 item_list_free(ItemList *item_list)
151 {
152 	ItemListCell *cell = NULL;
153 	ItemListCell *next_cell = NULL;
154 
155 	cell = item_list->head;
156 
157 	while (cell != NULL)
158 	{
159 		next_cell = cell->next;
160 		pfree(cell->string);
161 		pfree(cell);
162 		cell = next_cell;
163 	}
164 }
165 
166 
167 void
key_value_list_set(KeyValueList * item_list,const char * key,const char * value)168 key_value_list_set(KeyValueList *item_list, const char *key, const char *value)
169 {
170 	_key_value_list_set(item_list, false, key, value);
171 	return;
172 }
173 
174 void
key_value_list_replace_or_set(KeyValueList * item_list,const char * key,const char * value)175 key_value_list_replace_or_set(KeyValueList *item_list, const char *key, const char *value)
176 {
177 	_key_value_list_set(item_list, true, key, value);
178 	return;
179 }
180 
181 void
key_value_list_set_format(KeyValueList * item_list,const char * key,const char * value,...)182 key_value_list_set_format(KeyValueList *item_list, const char *key, const char *value, ...)
183 {
184 	va_list		arglist;
185 	char formatted_value[MAXLEN];
186 
187 	va_start(arglist, value);
188 	(void) xvsnprintf(formatted_value, MAXLEN, value, arglist);
189 	va_end(arglist);
190 
191 	return _key_value_list_set(item_list, false, key, formatted_value);
192 }
193 
194 static void
_key_value_list_set(KeyValueList * item_list,bool replace,const char * key,const char * value)195 _key_value_list_set(KeyValueList *item_list, bool replace, const char *key, const char *value)
196 {
197 	KeyValueListCell *cell = NULL;
198 	int			keylen = 0;
199 	int			vallen = 0;
200 
201 	if (replace == true)
202 	{
203 		KeyValueListCell *prev_cell = NULL;
204 		KeyValueListCell *next_cell = NULL;
205 
206 
207 		for (cell = item_list->head; cell; cell = next_cell)
208 		{
209 			next_cell = cell->next;
210 
211 			if (strcmp(cell->key, key) == 0)
212 			{
213 				if (item_list->head == cell)
214 					item_list->head = cell->next;
215 
216 				if (prev_cell)
217 				{
218 					prev_cell->next = cell->next;
219 
220 					if (item_list->tail == cell)
221 						item_list->tail = prev_cell;
222 				}
223 				else if (item_list->tail == cell)
224 				{
225 					item_list->tail = NULL;
226 				}
227 
228 				pfree(cell->key);
229 				pfree(cell->value);
230 				pfree(cell);
231 			}
232 			else
233 			{
234 				prev_cell = cell;
235 			}
236 		}
237 	}
238 
239 	cell = (KeyValueListCell *) pg_malloc0(sizeof(KeyValueListCell));
240 
241 	if (cell == NULL)
242 	{
243 		log_error(_("unable to allocate memory; terminating."));
244 		exit(ERR_BAD_CONFIG);
245 	}
246 
247 	keylen = strlen(key);
248 	vallen = strlen(value);
249 
250 	cell->key = pg_malloc0(keylen + 1);
251 	cell->value = pg_malloc0(vallen + 1);
252 	cell->output_mode = OM_NOT_SET;
253 
254 	strncpy(cell->key, key, keylen);
255 	strncpy(cell->value, value, vallen);
256 
257 	if (item_list->tail)
258 		item_list->tail->next = cell;
259 	else
260 		item_list->head = cell;
261 
262 	item_list->tail = cell;
263 
264 	return;
265 }
266 
267 
268 void
key_value_list_set_output_mode(KeyValueList * item_list,const char * key,OutputMode mode)269 key_value_list_set_output_mode(KeyValueList *item_list, const char *key, OutputMode mode)
270 {
271 	KeyValueListCell *cell = NULL;
272 
273 	for (cell = item_list->head; cell; cell = cell->next)
274 	{
275 		if (strncmp(key, cell->key, MAXLEN) == 0)
276 			cell->output_mode = mode;
277 	}
278 }
279 
280 const char *
key_value_list_get(KeyValueList * item_list,const char * key)281 key_value_list_get(KeyValueList *item_list, const char *key)
282 {
283 	return NULL;
284 }
285 
286 
287 void
key_value_list_free(KeyValueList * item_list)288 key_value_list_free(KeyValueList *item_list)
289 {
290 	KeyValueListCell *cell;
291 	KeyValueListCell *next_cell;
292 
293 	cell = item_list->head;
294 
295 	while (cell != NULL)
296 	{
297 		next_cell = cell->next;
298 		pfree(cell->key);
299 		pfree(cell->value);
300 		pfree(cell);
301 		cell = next_cell;
302 	}
303 }
304 
305 
306 void
check_status_list_set(CheckStatusList * list,const char * item,CheckStatus status,const char * details)307 check_status_list_set(CheckStatusList *list, const char *item, CheckStatus status, const char *details)
308 {
309 	check_status_list_set_format(list, item, status, "%s", details);
310 }
311 
312 
313 void
check_status_list_set_format(CheckStatusList * list,const char * item,CheckStatus status,const char * details,...)314 check_status_list_set_format(CheckStatusList *list, const char *item, CheckStatus status, const char *details,...)
315 {
316 	CheckStatusListCell *cell;
317 	va_list		arglist;
318 	int			itemlen;
319 
320 	cell = (CheckStatusListCell *) pg_malloc0(sizeof(CheckStatusListCell));
321 
322 	if (cell == NULL)
323 	{
324 		log_error(_("unable to allocate memory; terminating."));
325 		exit(ERR_BAD_CONFIG);
326 	}
327 
328 	itemlen = strlen(item);
329 
330 	cell->item = pg_malloc0(itemlen + 1);
331 	cell->details = pg_malloc0(MAXLEN);
332 	cell->status = status;
333 
334 	strncpy(cell->item, item, itemlen);
335 
336 	va_start(arglist, details);
337 	(void) xvsnprintf(cell->details, MAXLEN, details, arglist);
338 	va_end(arglist);
339 
340 
341 	if (list->tail)
342 		list->tail->next = cell;
343 	else
344 		list->head = cell;
345 
346 	list->tail = cell;
347 
348 	return;
349 
350 }
351 
352 
353 void
check_status_list_free(CheckStatusList * list)354 check_status_list_free(CheckStatusList *list)
355 {
356 	CheckStatusListCell *cell = NULL;
357 	CheckStatusListCell *next_cell = NULL;
358 
359 	cell = list->head;
360 
361 	while (cell != NULL)
362 	{
363 		next_cell = cell->next;
364 		pfree(cell->item);
365 		pfree(cell->details);
366 		pfree(cell);
367 		cell = next_cell;
368 	}
369 }
370 
371 
372 
373 const char *
output_check_status(CheckStatus status)374 output_check_status(CheckStatus status)
375 {
376 	switch (status)
377 	{
378 		case CHECK_STATUS_OK:
379 			return "OK";
380 		case CHECK_STATUS_WARNING:
381 			return "WARNING";
382 		case CHECK_STATUS_CRITICAL:
383 			return "CRITICAL";
384 		case CHECK_STATUS_UNKNOWN:
385 			return "UNKNOWN";
386 	}
387 
388 	return "UNKNOWN";
389 }
390 
391 
392 /*
393  * Escape a string for use as a parameter in recovery.conf
394  * Caller must free returned value
395  */
396 char *
escape_recovery_conf_value(const char * src)397 escape_recovery_conf_value(const char *src)
398 {
399 	char	   *result = escape_single_quotes_ascii(src);
400 
401 	if (!result)
402 	{
403 		fprintf(stderr, _("%s: out of memory\n"), progname());
404 		exit(ERR_INTERNAL);
405 	}
406 	return result;
407 }
408 
409 
410 char *
escape_string(PGconn * conn,const char * string)411 escape_string(PGconn *conn, const char *string)
412 {
413 	char	   *escaped_string;
414 	int			error;
415 
416 	escaped_string = pg_malloc0(MAXLEN);
417 
418 	(void) PQescapeStringConn(conn, escaped_string, string, MAXLEN, &error);
419 
420 	if (error)
421 	{
422 		pfree(escaped_string);
423 		return NULL;
424 	}
425 
426 	return escaped_string;
427 }
428 
429 
430 /*
431  * simple function to escape double quotes only
432  */
433 void
escape_double_quotes(char * string,PQExpBufferData * out)434 escape_double_quotes(char *string, PQExpBufferData *out)
435 {
436 	char *ptr;
437 
438 	for (ptr = string; *ptr; ptr++)
439 	{
440 		if (*ptr == '"')
441 		{
442 			if ( (ptr == string) || (ptr > string && *(ptr - 1) != '\\'))
443 			{
444 				appendPQExpBufferChar(out, '\\');
445 			}
446 		}
447 		appendPQExpBufferChar(out, *ptr);
448 	}
449 
450 	return;
451 }
452 
453 
454 char *
string_skip_prefix(const char * prefix,char * string)455 string_skip_prefix(const char *prefix, char *string)
456 {
457 	int			n;
458 
459 	n = strlen(prefix);
460 
461 	if (strncmp(prefix, string, n))
462 		return NULL;
463 	else
464 		return string + n;
465 }
466 
467 
468 char *
string_remove_trailing_newlines(char * string)469 string_remove_trailing_newlines(char *string)
470 {
471 	int			n;
472 
473 	n = strlen(string) - 1;
474 
475 	while (n >= 0 && string[n] == '\n')
476 		string[n] = 0;
477 
478 	return string;
479 }
480 
481 
482 char *
trim(char * s)483 trim(char *s)
484 {
485 	/* Initialize start, end pointers */
486 	char	   *s1 = s,
487 			   *s2 = &s[strlen(s) - 1];
488 
489 	/* If string is empty, no action needed */
490 	if (s2 < s1)
491 		return s;
492 
493 	/* Trim and delimit right side */
494 	while ((isspace(*s2)) && (s2 >= s1))
495 		--s2;
496 	*(s2 + 1) = '\0';
497 
498 	/* String is all whitespace - no need for further processing */
499 	if (s2 + 1 == s1)
500 		return s;
501 
502 	/* Trim left side */
503 	while ((isspace(*s1)) && (s1 < s2))
504 		++s1;
505 
506 	/* Copy finished string */
507 	memmove(s, s1, (s2 - s1) + 1);
508 	s[s2 - s1 + 1] = '\0';
509 
510 	return s;
511 }
512 
513 
514 void
parse_follow_command(char * parsed_command,char * template,int node_id)515 parse_follow_command(char *parsed_command, char *template, int node_id)
516 {
517 	const char *src_ptr = NULL;
518 	char	   *dst_ptr = NULL;
519 	char	   *end_ptr = NULL;
520 
521 	dst_ptr = parsed_command;
522 	end_ptr = parsed_command + MAXPGPATH - 1;
523 	*end_ptr = '\0';
524 
525 	for (src_ptr = template; *src_ptr; src_ptr++)
526 	{
527 		if (*src_ptr == '%')
528 		{
529 			switch (src_ptr[1])
530 			{
531 				case '%':
532 					/* %%: replace with % */
533 					if (dst_ptr < end_ptr)
534 					{
535 						src_ptr++;
536 						*dst_ptr++ = *src_ptr;
537 					}
538 					break;
539 				case 'n':
540 					/* %n: node id */
541 					src_ptr++;
542 					snprintf(dst_ptr, end_ptr - dst_ptr, "%i", node_id);
543 					dst_ptr += strlen(dst_ptr);
544 					break;
545 				default:
546 					/* otherwise treat the % as not special */
547 					if (dst_ptr < end_ptr)
548 						*dst_ptr++ = *src_ptr;
549 					break;
550 			}
551 		}
552 		else
553 		{
554 			if (dst_ptr < end_ptr)
555 				*dst_ptr++ = *src_ptr;
556 		}
557 	}
558 
559 	*dst_ptr = '\0';
560 
561 	return;
562 }
563 
564 
565 const char *
format_bool(bool value)566 format_bool(bool value)
567 {
568 	return value == true ? "true" : "false";
569 }
570