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-2025, 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
19static 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 */
31void
32report_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
42void
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");
54 }
55 else if (log_opts.verbose)
57}
58
59/*
60 * Remove any logs generated internally. To be used once when exiting.
61 */
62void
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 */
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",
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 */
128void
129prep_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 */
155void
156prep_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
175static void
176pg_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);
198 fflush(log_opts.internal);
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 ? "" : "...",
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
258void
259pg_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
269void
270pg_fatal(const char *fmt,...)
271{
272 va_list args;
273
274 va_start(args, fmt);
275 pg_log_v(PG_FATAL, fmt, args);
276 va_end(args);
277 /* NOTREACHED */
278 printf(_("Failure, exiting\n"));
279 exit(1);
280}
281
282
283void
285{
286 /* all seems well */
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 */
298char *
299quote_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 */
322int
323get_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 */
351unsigned int
352str2uint(const char *str)
353{
354 return strtoul(str, NULL, 10);
355}
void pg_fatal(const char *fmt,...)
Definition: util.c:270
char * quote_identifier(const char *s)
Definition: util.c:299
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
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 pg_attribute_printf(f, a)
Definition: c.h:233
#define fprintf(file, fmt, msg)
Definition: cubescan.l:21
#define _(x)
Definition: elog.c:90
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
Assert(PointerIsAligned(start, uint64))
const char * str
#define MESSAGE_WIDTH
Definition: pg_upgrade.h:25
#define QUERY_ALLOC
Definition: pg_upgrade.h:23
eLogType
Definition: pg_upgrade.h:271
@ PG_FATAL
Definition: pg_upgrade.h:277
@ PG_STATUS
Definition: pg_upgrade.h:273
@ PG_WARNING
Definition: pg_upgrade.h:276
@ PG_REPORT_NONL
Definition: pg_upgrade.h:274
@ PG_VERBOSE
Definition: pg_upgrade.h:272
@ PG_REPORT
Definition: pg_upgrade.h:275
#define MAX_STRING
Definition: pg_upgrade.h:22
#define vsnprintf
Definition: port.h:238
int pg_check_dir(const char *dir)
Definition: pgcheckdir.c:33
#define printf(...)
Definition: port.h:245
bool rmtree(const char *path, bool rmtopdir)
Definition: rmtree.c:50
char * rootdir
Definition: pg_upgrade.h:316
bool retain
Definition: pg_upgrade.h:314
FILE * internal
Definition: pg_upgrade.h:312
char * basedir
Definition: pg_upgrade.h:317
bool isatty
Definition: pg_upgrade.h:320
bool verbose
Definition: pg_upgrade.h:313
const char * get_user_name(char **errstr)
Definition: username.c:31
const char * type