PostgreSQL Source Code  git master
util.c
Go to the documentation of this file.
1 /*
2  * util.c
3  *
4  * utility functions
5  *
6  * Copyright (c) 2010-2022, PostgreSQL Global Development Group
7  * src/bin/pg_upgrade/util.c
8  */
9 
10 #include "postgres_fe.h"
11 
12 #include <signal.h>
13 
14 #include "common/username.h"
15 #include "pg_upgrade.h"
16 
18 
19 static void pg_log_v(eLogType type, const char *fmt, va_list ap) pg_attribute_printf(2, 0);
20 
21 
22 /*
23  * report_status()
24  *
25  * Displays the result of an operation (ok, failed, error message,...)
26  *
27  * This is no longer functionally different from pg_log(), but we keep
28  * it around to maintain a notational distinction between operation
29  * results and other messages.
30  */
31 void
32 report_status(eLogType type, const char *fmt,...)
33 {
34  va_list args;
35 
36  va_start(args, fmt);
37  pg_log_v(type, fmt, args);
38  va_end(args);
39 }
40 
41 
42 void
44 {
45  /*
46  * For output to a tty, erase prior contents of progress line. When either
47  * tty or verbose, indent so that report_status() output will align
48  * nicely.
49  */
50  if (log_opts.isatty)
51  {
52  printf("\r");
53  pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, "");
54  }
55  else if (log_opts.verbose)
56  pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, "");
57 }
58 
59 /*
60  * Remove any logs generated internally. To be used once when exiting.
61  */
62 void
64 {
65  fclose(log_opts.internal);
66 
67  /* Remove dump and log files? */
68  if (log_opts.retain)
69  return;
70 
71  (void) rmtree(log_opts.basedir, true);
72 
73  /* Remove pg_upgrade_output.d only if empty */
74  switch (pg_check_dir(log_opts.rootdir))
75  {
76  case 0: /* non-existent */
77  case 3: /* exists and contains a mount point */
78  Assert(false);
79  break;
80 
81  case 1: /* exists and empty */
82  case 2: /* exists and contains only dot files */
83  (void) rmtree(log_opts.rootdir, true);
84  break;
85 
86  case 4: /* exists */
87 
88  /*
89  * Keep the root directory as this includes some past log
90  * activity.
91  */
92  break;
93 
94  default:
95  /* different failure, just report it */
96  pg_log(PG_WARNING, "could not access directory \"%s\": %m",
98  break;
99  }
100 }
101 
102 /*
103  * prep_status
104  *
105  * Displays a message that describes an operation we are about to begin.
106  * We pad the message out to MESSAGE_WIDTH characters so that all of the
107  * "ok" and "failed" indicators line up nicely. (Overlength messages
108  * will be truncated, so don't get too verbose.)
109  *
110  * A typical sequence would look like this:
111  * prep_status("about to flarb the next %d files", fileCount);
112  * if ((message = flarbFiles(fileCount)) == NULL)
113  * report_status(PG_REPORT, "ok");
114  * else
115  * pg_log(PG_FATAL, "failed: %s", message);
116  */
117 void
118 prep_status(const char *fmt,...)
119 {
120  va_list args;
121  char message[MAX_STRING];
122 
123  va_start(args, fmt);
124  vsnprintf(message, sizeof(message), fmt, args);
125  va_end(args);
126 
127  /* trim strings */
128  pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
129 }
130 
131 /*
132  * prep_status_progress
133  *
134  * Like prep_status(), but for potentially longer running operations.
135  * Details about what item is currently being processed can be displayed
136  * with pg_log(PG_STATUS, ...). A typical sequence would look like this:
137  *
138  * prep_status_progress("copying files");
139  * for (...)
140  * pg_log(PG_STATUS, "%s", filename);
141  * end_progress_output();
142  * report_status(PG_REPORT, "ok");
143  */
144 void
145 prep_status_progress(const char *fmt,...)
146 {
147  va_list args;
148  char message[MAX_STRING];
149 
150  va_start(args, fmt);
151  vsnprintf(message, sizeof(message), fmt, args);
152  va_end(args);
153 
154  /*
155  * If outputting to a tty or in verbose, append newline. pg_log_v() will
156  * put the individual progress items onto the next line.
157  */
159  pg_log(PG_REPORT, "%-*s", MESSAGE_WIDTH, message);
160  else
161  pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
162 }
163 
164 static void
165 pg_log_v(eLogType type, const char *fmt, va_list ap)
166 {
167  char message[QUERY_ALLOC];
168 
169  /* No incoming message should end in newline; we add that here. */
170  Assert(fmt);
171  Assert(fmt[0] == '\0' || fmt[strlen(fmt) - 1] != '\n');
172 
173  vsnprintf(message, sizeof(message), _(fmt), ap);
174 
175  /* PG_VERBOSE and PG_STATUS are only output in verbose mode */
176  /* fopen() on log_opts.internal might have failed, so check it */
177  if (((type != PG_VERBOSE && type != PG_STATUS) || log_opts.verbose) &&
178  log_opts.internal != NULL)
179  {
180  if (type == PG_STATUS)
181  /* status messages get two leading spaces, see below */
182  fprintf(log_opts.internal, " %s\n", message);
183  else if (type == PG_REPORT_NONL)
184  fprintf(log_opts.internal, "%s", message);
185  else
186  fprintf(log_opts.internal, "%s\n", message);
188  }
189 
190  switch (type)
191  {
192  case PG_VERBOSE:
193  if (log_opts.verbose)
194  printf("%s\n", message);
195  break;
196 
197  case PG_STATUS:
198 
199  /*
200  * For output to a terminal, we add two leading spaces and no
201  * newline; instead append \r so that the next message is output
202  * on the same line. Truncate on the left to fit into
203  * MESSAGE_WIDTH (counting the spaces as part of that).
204  *
205  * If going to non-interactive output, only display progress if
206  * verbose is enabled. Otherwise the output gets unreasonably
207  * large by default.
208  */
209  if (log_opts.isatty)
210  {
211  bool itfits = (strlen(message) <= MESSAGE_WIDTH - 2);
212 
213  /* prefix with "..." if we do leading truncation */
214  printf(" %s%-*.*s\r",
215  itfits ? "" : "...",
216  MESSAGE_WIDTH - 2, MESSAGE_WIDTH - 2,
217  itfits ? message :
218  message + strlen(message) - MESSAGE_WIDTH + 3 + 2);
219  }
220  else if (log_opts.verbose)
221  printf(" %s\n", message);
222  break;
223 
224  case PG_REPORT_NONL:
225  /* This option is for use by prep_status and friends */
226  printf("%s", message);
227  break;
228 
229  case PG_REPORT:
230  case PG_WARNING:
231  printf("%s\n", message);
232  break;
233 
234  case PG_FATAL:
235  /* Extra newline in case we're interrupting status output */
236  printf("\n%s\n", message);
237  printf(_("Failure, exiting\n"));
238  exit(1);
239  break;
240 
241  /* No default:, we want a warning for omitted cases */
242  }
243  fflush(stdout);
244 }
245 
246 
247 void
248 pg_log(eLogType type, const char *fmt,...)
249 {
250  va_list args;
251 
252  va_start(args, fmt);
253  pg_log_v(type, fmt, args);
254  va_end(args);
255 }
256 
257 
258 void
259 pg_fatal(const char *fmt,...)
260 {
261  va_list args;
262 
263  va_start(args, fmt);
265  va_end(args);
266  /* NOTREACHED */
267  printf(_("Failure, exiting\n"));
268  exit(1);
269 }
270 
271 
272 void
273 check_ok(void)
274 {
275  /* all seems well */
276  report_status(PG_REPORT, "ok");
277 }
278 
279 
280 /*
281  * quote_identifier()
282  * Properly double-quote a SQL identifier.
283  *
284  * The result should be pg_free'd, but most callers don't bother because
285  * memory leakage is not a big deal in this program.
286  */
287 char *
288 quote_identifier(const char *s)
289 {
290  char *result = pg_malloc(strlen(s) * 2 + 3);
291  char *r = result;
292 
293  *r++ = '"';
294  while (*s)
295  {
296  if (*s == '"')
297  *r++ = *s;
298  *r++ = *s;
299  s++;
300  }
301  *r++ = '"';
302  *r++ = '\0';
303 
304  return result;
305 }
306 
307 
308 /*
309  * get_user_info()
310  */
311 int
312 get_user_info(char **user_name_p)
313 {
314  int user_id;
315  const char *user_name;
316  char *errstr;
317 
318 #ifndef WIN32
319  user_id = geteuid();
320 #else
321  user_id = 1;
322 #endif
323 
324  user_name = get_user_name(&errstr);
325  if (!user_name)
326  pg_fatal("%s", errstr);
327 
328  /* make a copy */
329  *user_name_p = pg_strdup(user_name);
330 
331  return user_id;
332 }
333 
334 
335 /*
336  * str2uint()
337  *
338  * convert string to oid
339  */
340 unsigned int
341 str2uint(const char *str)
342 {
343  return strtoul(str, NULL, 10);
344 }
#define pg_attribute_printf(f, a)
Definition: c.h:175
#define _(x)
Definition: elog.c:90
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
static void const char * fmt
static void const char fflush(stdout)
va_end(args)
Assert(fmt[strlen(fmt) - 1] !='\n')
exit(1)
va_start(args, fmt)
#define MESSAGE_WIDTH
Definition: pg_upgrade.h:25
#define QUERY_ALLOC
Definition: pg_upgrade.h:23
eLogType
Definition: pg_upgrade.h:237
@ PG_FATAL
Definition: pg_upgrade.h:243
@ PG_STATUS
Definition: pg_upgrade.h:239
@ PG_WARNING
Definition: pg_upgrade.h:242
@ PG_REPORT_NONL
Definition: pg_upgrade.h:240
@ PG_VERBOSE
Definition: pg_upgrade.h:238
@ PG_REPORT
Definition: pg_upgrade.h:241
#define MAX_STRING
Definition: pg_upgrade.h:22
#define vsnprintf
Definition: port.h:237
int pg_check_dir(const char *dir)
Definition: pgcheckdir.c:33
#define fprintf
Definition: port.h:242
#define printf(...)
Definition: port.h:244
bool rmtree(const char *path, bool rmtopdir)
Definition: rmtree.c:42
char * rootdir
Definition: pg_upgrade.h:280
bool retain
Definition: pg_upgrade.h:278
FILE * internal
Definition: pg_upgrade.h:276
char * basedir
Definition: pg_upgrade.h:281
bool isatty
Definition: pg_upgrade.h:284
bool verbose
Definition: pg_upgrade.h:277
const char * get_user_name(char **errstr)
Definition: username.c:31
void pg_fatal(const char *fmt,...)
Definition: util.c:259
unsigned int str2uint(const char *str)
Definition: util.c:341
void pg_log(eLogType type, const char *fmt,...)
Definition: util.c:248
void prep_status_progress(const char *fmt,...)
Definition: util.c:145
int get_user_info(char **user_name_p)
Definition: util.c:312
void cleanup_output_dirs(void)
Definition: util.c:63
static void void report_status(eLogType type, const char *fmt,...)
Definition: util.c:32
LogOpts log_opts
Definition: util.c:17
char * quote_identifier(const char *s)
Definition: util.c:288
void check_ok(void)
Definition: util.c:273
void prep_status(const char *fmt,...)
Definition: util.c:118
static void pg_log_v(eLogType type, const char *fmt, va_list ap) pg_attribute_printf(2
Definition: util.c:165
void end_progress_output(void)
Definition: util.c:43