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