Age Owner Branch data TLA Line data Source code
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 : :
17 : : LogOpts log_opts;
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
5436 bruce@momjian.us 32 :GIC 367 : report_status(eLogType type, const char *fmt,...)
33 : : {
34 : : va_list args;
35 : :
5596 36 : 367 : va_start(args, fmt);
1152 tgl@sss.pgh.pa.us 37 : 367 : pg_log_v(type, fmt, args);
5596 bruce@momjian.us 38 : 366 : va_end(args);
39 : 366 : }
40 : :
41 : :
42 : : void
4663 43 : 27 : end_progress_output(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 : : */
1293 andres@anarazel.de 50 [ - + ]: 27 : if (log_opts.isatty)
51 : : {
1202 peter@eisentraut.org 52 :UIC 0 : printf("\r");
1152 tgl@sss.pgh.pa.us 53 : 0 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, "");
54 : : }
1293 andres@anarazel.de 55 [ - + ]:GIC 27 : else if (log_opts.verbose)
1152 tgl@sss.pgh.pa.us 56 :UIC 0 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, "");
4663 bruce@momjian.us 57 :GIC 27 : }
58 : :
59 : : /*
60 : : * Remove any logs generated internally. To be used once when exiting.
61 : : */
62 : : void
1186 michael@paquier.xyz 63 : 9 : cleanup_output_dirs(void)
64 : : {
65 : 9 : fclose(log_opts.internal);
66 : :
67 : : /* Remove dump and log files? */
68 [ - + ]: 9 : if (log_opts.retain)
1186 michael@paquier.xyz 69 :UIC 0 : return;
70 : :
71 : : /*
72 : : * Try twice. The second time might wait for files to finish being
73 : : * unlinked, on Windows.
74 : : */
948 tmunro@postgresql.or 75 [ - + ]:GIC 9 : if (!rmtree(log_opts.basedir, true))
948 tmunro@postgresql.or 76 :UIC 0 : rmtree(log_opts.basedir, true);
77 : :
78 : : /* Remove pg_upgrade_output.d only if empty */
1186 michael@paquier.xyz 79 [ - + + - ]:GIC 9 : switch (pg_check_dir(log_opts.rootdir))
80 : : {
1186 michael@paquier.xyz 81 :UIC 0 : case 0: /* non-existent */
82 : : case 3: /* exists and contains a mount point */
83 : 0 : Assert(false);
84 : : break;
85 : :
1186 michael@paquier.xyz 86 :GIC 7 : 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 : : */
948 tmunro@postgresql.or 93 [ - + ]: 7 : if (!rmtree(log_opts.rootdir, true))
948 tmunro@postgresql.or 94 :UIC 0 : rmtree(log_opts.rootdir, true);
1186 michael@paquier.xyz 95 :GIC 7 : break;
96 : :
97 : 2 : case 4: /* exists */
98 : :
99 : : /*
100 : : * Keep the root directory as this includes some past log
101 : : * activity.
102 : : */
103 : 2 : break;
104 : :
1186 michael@paquier.xyz 105 :UIC 0 : default:
106 : : /* different failure, just report it */
1152 tgl@sss.pgh.pa.us 107 : 0 : pg_log(PG_WARNING, "could not access directory \"%s\": %m",
108 : : log_opts.rootdir);
1186 michael@paquier.xyz 109 : 0 : 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
5436 bruce@momjian.us 129 :GIC 346 : prep_status(const char *fmt,...)
130 : : {
131 : : va_list args;
132 : : char message[MAX_STRING];
133 : :
5596 134 : 346 : va_start(args, fmt);
135 : 346 : vsnprintf(message, sizeof(message), fmt, args);
136 : 346 : va_end(args);
137 : :
138 : : /* trim strings */
1152 tgl@sss.pgh.pa.us 139 : 346 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
1293 andres@anarazel.de 140 : 346 : }
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 : 27 : prep_status_progress(const char *fmt,...)
157 : : {
158 : : va_list args;
159 : : char message[MAX_STRING];
160 : :
161 : 27 : va_start(args, fmt);
162 : 27 : vsnprintf(message, sizeof(message), fmt, args);
163 : 27 : 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 : : */
169 [ + - - + ]: 27 : if (log_opts.isatty || log_opts.verbose)
4656 bruce@momjian.us 170 :UIC 0 : pg_log(PG_REPORT, "%-*s", MESSAGE_WIDTH, message);
171 : : else
1152 tgl@sss.pgh.pa.us 172 :GIC 27 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
5596 bruce@momjian.us 173 : 27 : }
174 : :
175 : : static void
4358 peter_e@gmx.net 176 : 6583 : 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. */
1152 tgl@sss.pgh.pa.us 181 [ - + ]: 6583 : Assert(fmt);
182 [ + - - + ]: 6583 : Assert(fmt[0] == '\0' || fmt[strlen(fmt) - 1] != '\n');
183 : :
3225 peter_e@gmx.net 184 : 6583 : 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 */
4656 bruce@momjian.us 188 [ + + + + : 6583 : if (((type != PG_VERBOSE && type != PG_STATUS) || log_opts.verbose) &&
- + ]
189 [ + - ]: 811 : log_opts.internal != NULL)
190 : : {
191 [ - + ]: 811 : if (type == PG_STATUS)
192 : : /* status messages get two leading spaces, see below */
4656 bruce@momjian.us 193 :UIC 0 : fprintf(log_opts.internal, " %s\n", message);
1152 tgl@sss.pgh.pa.us 194 [ + + ]:GIC 811 : else if (type == PG_REPORT_NONL)
4656 bruce@momjian.us 195 : 373 : fprintf(log_opts.internal, "%s", message);
196 : : else
1152 tgl@sss.pgh.pa.us 197 : 438 : fprintf(log_opts.internal, "%s\n", message);
4926 bruce@momjian.us 198 : 811 : fflush(log_opts.internal);
199 : : }
200 : :
5596 201 [ + + + + : 6583 : switch (type)
+ - ]
202 : : {
4926 203 : 3888 : case PG_VERBOSE:
5435 204 [ - + ]: 3888 : if (log_opts.verbose)
1152 tgl@sss.pgh.pa.us 205 :UIC 0 : printf("%s\n", message);
5596 bruce@momjian.us 206 :GIC 3888 : break;
207 : :
4656 208 : 1884 : 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 : : */
1293 andres@anarazel.de 220 [ - + ]: 1884 : if (log_opts.isatty)
221 : : {
1152 tgl@sss.pgh.pa.us 222 :UIC 0 : bool itfits = (strlen(message) <= MESSAGE_WIDTH - 2);
223 : :
224 : : /* prefix with "..." if we do leading truncation */
225 [ # # # # ]: 0 : 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 : : }
1293 andres@anarazel.de 231 [ - + ]:GIC 1884 : else if (log_opts.verbose)
3225 peter_e@gmx.net 232 :UIC 0 : printf(" %s\n", message);
4656 bruce@momjian.us 233 :GIC 1884 : break;
234 : :
1152 tgl@sss.pgh.pa.us 235 : 373 : case PG_REPORT_NONL:
236 : : /* This option is for use by prep_status and friends */
237 : 373 : printf("%s", message);
238 : 373 : break;
239 : :
5596 bruce@momjian.us 240 : 429 : case PG_REPORT:
241 : : case PG_WARNING:
1152 tgl@sss.pgh.pa.us 242 : 429 : printf("%s\n", message);
5596 bruce@momjian.us 243 : 429 : break;
244 : :
245 : 9 : case PG_FATAL:
246 : : /* Extra newline in case we're interrupting status output */
1152 tgl@sss.pgh.pa.us 247 : 9 : printf("\n%s\n", message);
3249 peter_e@gmx.net 248 : 9 : printf(_("Failure, exiting\n"));
4259 249 : 9 : exit(1);
250 : : break;
251 : :
252 : : /* No default:, we want a warning for omitted cases */
253 : : }
5596 bruce@momjian.us 254 : 6574 : fflush(stdout);
255 : 6574 : }
256 : :
257 : :
258 : : void
4358 peter_e@gmx.net 259 : 6208 : pg_log(eLogType type, const char *fmt,...)
260 : : {
261 : : va_list args;
262 : :
263 : 6208 : va_start(args, fmt);
264 : 6208 : pg_log_v(type, fmt, args);
265 : 6208 : va_end(args);
266 : 6208 : }
267 : :
268 : :
269 : : void
270 : 8 : pg_fatal(const char *fmt,...)
271 : : {
272 : : va_list args;
273 : :
274 : 8 : va_start(args, fmt);
275 : 8 : pg_log_v(PG_FATAL, fmt, args);
4358 peter_e@gmx.net 276 :UIC 0 : va_end(args);
277 : : /* NOTREACHED */
3249 278 : 0 : printf(_("Failure, exiting\n"));
4358 279 : 0 : exit(1);
280 : : }
281 : :
282 : :
283 : : void
5436 bruce@momjian.us 284 :GIC 366 : check_ok(void)
285 : : {
286 : : /* all seems well */
287 : 366 : report_status(PG_REPORT, "ok");
5596 288 : 366 : }
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 *
5436 299 : 16 : quote_identifier(const char *s)
300 : : {
301 : 16 : char *result = pg_malloc(strlen(s) * 2 + 3);
5596 302 : 16 : char *r = result;
303 : :
304 : 16 : *r++ = '"';
305 [ + + ]: 160 : while (*s)
306 : : {
307 [ - + ]: 144 : if (*s == '"')
5596 bruce@momjian.us 308 :UIC 0 : *r++ = *s;
5596 bruce@momjian.us 309 :GIC 144 : *r++ = *s;
310 : 144 : s++;
311 : : }
312 : 16 : *r++ = '"';
313 : 16 : *r++ = '\0';
314 : :
315 : 16 : return result;
316 : : }
317 : :
318 : :
319 : : /*
320 : : * get_user_info()
321 : : */
322 : : int
4280 rhaas@postgresql.org 323 : 21 : get_user_info(char **user_name_p)
324 : : {
325 : : int user_id;
326 : : const char *user_name;
327 : : char *errstr;
328 : :
329 : : #ifndef WIN32
5596 bruce@momjian.us 330 : 21 : user_id = geteuid();
331 : : #else
332 : : user_id = 1;
333 : : #endif
334 : :
4280 rhaas@postgresql.org 335 : 21 : user_name = get_user_name(&errstr);
336 [ - + ]: 21 : if (!user_name)
1152 tgl@sss.pgh.pa.us 337 :UIC 0 : pg_fatal("%s", errstr);
338 : :
339 : : /* make a copy */
4280 rhaas@postgresql.org 340 :GIC 21 : *user_name_p = pg_strdup(user_name);
341 : :
5596 bruce@momjian.us 342 : 21 : return user_id;
343 : : }
344 : :
345 : :
346 : : /*
347 : : * str2uint()
348 : : *
349 : : * convert string to oid
350 : : */
351 : : unsigned int
5457 352 : 640 : str2uint(const char *str)
353 : : {
5456 354 : 640 : return strtoul(str, NULL, 10);
355 : : }
|