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-2024, 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  /*
72  * Try twice. The second time might wait for files to finish being
73  * unlinked, on Windows.
74  */
75  if (!rmtree(log_opts.basedir, true))
76  rmtree(log_opts.basedir, true);
77 
78  /* Remove pg_upgrade_output.d only if empty */
79  switch (pg_check_dir(log_opts.rootdir))
80  {
81  case 0: /* non-existent */
82  case 3: /* exists and contains a mount point */
83  Assert(false);
84  break;
85 
86  case 1: /* exists and empty */
87  case 2: /* exists and contains only dot files */
88 
89  /*
90  * Try twice. The second time might wait for files to finish
91  * being unlinked, on Windows.
92  */
93  if (!rmtree(log_opts.rootdir, true))
94  rmtree(log_opts.rootdir, true);
95  break;
96 
97  case 4: /* exists */
98 
99  /*
100  * Keep the root directory as this includes some past log
101  * activity.
102  */
103  break;
104 
105  default:
106  /* different failure, just report it */
107  pg_log(PG_WARNING, "could not access directory \"%s\": %m",
108  log_opts.rootdir);
109  break;
110  }
111 }
112 
113 /*
114  * prep_status
115  *
116  * Displays a message that describes an operation we are about to begin.
117  * We pad the message out to MESSAGE_WIDTH characters so that all of the
118  * "ok" and "failed" indicators line up nicely. (Overlength messages
119  * will be truncated, so don't get too verbose.)
120  *
121  * A typical sequence would look like this:
122  * prep_status("about to flarb the next %d files", fileCount);
123  * if ((message = flarbFiles(fileCount)) == NULL)
124  * report_status(PG_REPORT, "ok");
125  * else
126  * pg_log(PG_FATAL, "failed: %s", message);
127  */
128 void
129 prep_status(const char *fmt,...)
130 {
131  va_list args;
132  char message[MAX_STRING];
133 
134  va_start(args, fmt);
135  vsnprintf(message, sizeof(message), fmt, args);
136  va_end(args);
137 
138  /* trim strings */
139  pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
140 }
141 
142 /*
143  * prep_status_progress
144  *
145  * Like prep_status(), but for potentially longer running operations.
146  * Details about what item is currently being processed can be displayed
147  * with pg_log(PG_STATUS, ...). A typical sequence would look like this:
148  *
149  * prep_status_progress("copying files");
150  * for (...)
151  * pg_log(PG_STATUS, "%s", filename);
152  * end_progress_output();
153  * report_status(PG_REPORT, "ok");
154  */
155 void
156 prep_status_progress(const char *fmt,...)
157 {
158  va_list args;
159  char message[MAX_STRING];
160 
161  va_start(args, fmt);
162  vsnprintf(message, sizeof(message), fmt, args);
163  va_end(args);
164 
165  /*
166  * If outputting to a tty or in verbose, append newline. pg_log_v() will
167  * put the individual progress items onto the next line.
168  */
170  pg_log(PG_REPORT, "%-*s", MESSAGE_WIDTH, message);
171  else
172  pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
173 }
174 
175 static void
176 pg_log_v(eLogType type, const char *fmt, va_list ap)
177 {
178  char message[QUERY_ALLOC];
179 
180  /* No incoming message should end in newline; we add that here. */
181  Assert(fmt);
182  Assert(fmt[0] == '\0' || fmt[strlen(fmt) - 1] != '\n');
183 
184  vsnprintf(message, sizeof(message), _(fmt), ap);
185 
186  /* PG_VERBOSE and PG_STATUS are only output in verbose mode */
187  /* fopen() on log_opts.internal might have failed, so check it */
188  if (((type != PG_VERBOSE && type != PG_STATUS) || log_opts.verbose) &&
189  log_opts.internal != NULL)
190  {
191  if (type == PG_STATUS)
192  /* status messages get two leading spaces, see below */
193  fprintf(log_opts.internal, " %s\n", message);
194  else if (type == PG_REPORT_NONL)
195  fprintf(log_opts.internal, "%s", message);
196  else
197  fprintf(log_opts.internal, "%s\n", message);
199  }
200 
201  switch (type)
202  {
203  case PG_VERBOSE:
204  if (log_opts.verbose)
205  printf("%s\n", message);
206  break;
207 
208  case PG_STATUS:
209 
210  /*
211  * For output to a terminal, we add two leading spaces and no
212  * newline; instead append \r so that the next message is output
213  * on the same line. Truncate on the left to fit into
214  * MESSAGE_WIDTH (counting the spaces as part of that).
215  *
216  * If going to non-interactive output, only display progress if
217  * verbose is enabled. Otherwise the output gets unreasonably
218  * large by default.
219  */
220  if (log_opts.isatty)
221  {
222  bool itfits = (strlen(message) <= MESSAGE_WIDTH - 2);
223 
224  /* prefix with "..." if we do leading truncation */
225  printf(" %s%-*.*s\r",
226  itfits ? "" : "...",
227  MESSAGE_WIDTH - 2, MESSAGE_WIDTH - 2,
228  itfits ? message :
229  message + strlen(message) - MESSAGE_WIDTH + 3 + 2);
230  }
231  else if (log_opts.verbose)
232  printf(" %s\n", message);
233  break;
234 
235  case PG_REPORT_NONL:
236  /* This option is for use by prep_status and friends */
237  printf("%s", message);
238  break;
239 
240  case PG_REPORT:
241  case PG_WARNING:
242  printf("%s\n", message);
243  break;
244 
245  case PG_FATAL:
246  /* Extra newline in case we're interrupting status output */
247  printf("\n%s\n", message);
248  printf(_("Failure, exiting\n"));
249  exit(1);
250  break;
251 
252  /* No default:, we want a warning for omitted cases */
253  }
254  fflush(stdout);
255 }
256 
257 
258 void
259 pg_log(eLogType type, const char *fmt,...)
260 {
261  va_list args;
262 
263  va_start(args, fmt);
264  pg_log_v(type, fmt, args);
265  va_end(args);
266 }
267 
268 
269 void
270 pg_fatal(const char *fmt,...)
271 {
272  va_list args;
273 
274  va_start(args, fmt);
276  va_end(args);
277  /* NOTREACHED */
278  printf(_("Failure, exiting\n"));
279  exit(1);
280 }
281 
282 
283 void
284 check_ok(void)
285 {
286  /* all seems well */
287  report_status(PG_REPORT, "ok");
288 }
289 
290 
291 /*
292  * quote_identifier()
293  * Properly double-quote a SQL identifier.
294  *
295  * The result should be pg_free'd, but most callers don't bother because
296  * memory leakage is not a big deal in this program.
297  */
298 char *
299 quote_identifier(const char *s)
300 {
301  char *result = pg_malloc(strlen(s) * 2 + 3);
302  char *r = result;
303 
304  *r++ = '"';
305  while (*s)
306  {
307  if (*s == '"')
308  *r++ = *s;
309  *r++ = *s;
310  s++;
311  }
312  *r++ = '"';
313  *r++ = '\0';
314 
315  return result;
316 }
317 
318 
319 /*
320  * get_user_info()
321  */
322 int
323 get_user_info(char **user_name_p)
324 {
325  int user_id;
326  const char *user_name;
327  char *errstr;
328 
329 #ifndef WIN32
330  user_id = geteuid();
331 #else
332  user_id = 1;
333 #endif
334 
335  user_name = get_user_name(&errstr);
336  if (!user_name)
337  pg_fatal("%s", errstr);
338 
339  /* make a copy */
340  *user_name_p = pg_strdup(user_name);
341 
342  return user_id;
343 }
344 
345 
346 /*
347  * str2uint()
348  *
349  * convert string to oid
350  */
351 unsigned int
352 str2uint(const char *str)
353 {
354  return strtoul(str, NULL, 10);
355 }
void pg_fatal(const char *fmt,...)
Definition: util.c:270
unsigned int str2uint(const char *str)
Definition: util.c:352
void pg_log(eLogType type, const char *fmt,...)
Definition: util.c:259
void prep_status_progress(const char *fmt,...)
Definition: util.c:156
int get_user_info(char **user_name_p)
Definition: util.c:323
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:299
void check_ok(void)
Definition: util.c:284
void prep_status(const char *fmt,...)
Definition: util.c:129
static void pg_log_v(eLogType type, const char *fmt, va_list ap) pg_attribute_printf(2
Definition: util.c:176
void end_progress_output(void)
Definition: util.c:43
#define Assert(condition)
Definition: c.h:812
#define pg_attribute_printf(f, a)
Definition: c.h:210
#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
const char * str
static void const char * fmt
static void const char fflush(stdout)
va_end(args)
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:265
@ PG_FATAL
Definition: pg_upgrade.h:271
@ PG_STATUS
Definition: pg_upgrade.h:267
@ PG_WARNING
Definition: pg_upgrade.h:270
@ PG_REPORT_NONL
Definition: pg_upgrade.h:268
@ PG_VERBOSE
Definition: pg_upgrade.h:266
@ PG_REPORT
Definition: pg_upgrade.h:269
#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:50
char * rootdir
Definition: pg_upgrade.h:310
bool retain
Definition: pg_upgrade.h:308
FILE * internal
Definition: pg_upgrade.h:306
char * basedir
Definition: pg_upgrade.h:311
bool isatty
Definition: pg_upgrade.h:314
bool verbose
Definition: pg_upgrade.h:307
const char * get_user_name(char **errstr)
Definition: username.c:31
const char * type