Age Owner TLA Line data Source code
1 : /*
2 : * util.c
3 : *
4 : * utility functions
5 : *
6 : * Copyright (c) 2010-2023, 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
4555 bruce 32 GIC 45 : report_status(eLogType type, const char *fmt,...)
33 : {
34 : va_list args;
4715 bruce 35 ECB :
4715 bruce 36 GIC 45 : va_start(args, fmt);
271 tgl 37 GNC 45 : pg_log_v(type, fmt, args);
4715 bruce 38 GIC 44 : va_end(args);
4715 bruce 39 CBC 44 : }
4715 bruce 40 ECB :
41 :
42 : void
3782 bruce 43 GIC 3 : end_progress_output(void)
3782 bruce 44 ECB : {
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 : */
412 andres 50 GIC 3 : if (log_opts.isatty)
321 peter 51 ECB : {
321 peter 52 UIC 0 : printf("\r");
271 tgl 53 UNC 0 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, "");
321 peter 54 EUB : }
412 andres 55 GIC 3 : else if (log_opts.verbose)
271 tgl 56 UNC 0 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, "");
3782 bruce 57 GBC 3 : }
3782 bruce 58 ECB :
59 : /*
60 : * Remove any logs generated internally. To be used once when exiting.
61 : */
62 : void
305 michael 63 GIC 2 : cleanup_output_dirs(void)
305 michael 64 ECB : {
305 michael 65 GIC 2 : fclose(log_opts.internal);
305 michael 66 ECB :
67 : /* Remove dump and log files? */
305 michael 68 GIC 2 : if (log_opts.retain)
305 michael 69 LBC 0 : return;
305 michael 70 EUB :
71 : /*
72 : * Try twice. The second time might wait for files to finish being
73 : * unlinked, on Windows.
74 : */
67 tmunro 75 GNC 2 : if (!rmtree(log_opts.basedir, true))
67 tmunro 76 UNC 0 : rmtree(log_opts.basedir, true);
77 :
78 : /* Remove pg_upgrade_output.d only if empty */
305 michael 79 GIC 2 : switch (pg_check_dir(log_opts.rootdir))
80 : {
305 michael 81 LBC 0 : case 0: /* non-existent */
305 michael 82 EUB : case 3: /* exists and contains a mount point */
305 michael 83 UIC 0 : Assert(false);
84 : break;
305 michael 85 ECB :
305 michael 86 GIC 2 : case 1: /* exists and empty */
305 michael 87 EUB : 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 : */
67 tmunro 93 GNC 2 : if (!rmtree(log_opts.rootdir, true))
67 tmunro 94 UNC 0 : rmtree(log_opts.rootdir, true);
305 michael 95 GBC 2 : break;
96 :
305 michael 97 UIC 0 : case 4: /* exists */
305 michael 98 ECB :
99 : /*
100 : * Keep the root directory as this includes some past log
101 : * activity.
102 : */
305 michael 103 UIC 0 : break;
104 :
305 michael 105 LBC 0 : default:
305 michael 106 EUB : /* different failure, just report it */
271 tgl 107 UNC 0 : pg_log(PG_WARNING, "could not access directory \"%s\": %m",
108 : log_opts.rootdir);
305 michael 109 UBC 0 : break;
110 : }
111 : }
112 :
113 : /*
114 : * prep_status
4715 bruce 115 EUB : *
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
4555 bruce 129 GIC 41 : prep_status(const char *fmt,...)
130 : {
131 : va_list args;
132 : char message[MAX_STRING];
133 :
4715 134 41 : va_start(args, fmt);
135 41 : vsnprintf(message, sizeof(message), fmt, args);
136 41 : va_end(args);
137 :
138 : /* trim strings */
271 tgl 139 GNC 41 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
412 andres 140 GIC 41 : }
412 andres 141 ECB :
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
412 andres 156 GIC 3 : prep_status_progress(const char *fmt,...)
157 : {
158 : va_list args;
159 : char message[MAX_STRING];
160 :
161 3 : va_start(args, fmt);
162 3 : vsnprintf(message, sizeof(message), fmt, args);
163 3 : 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.
412 andres 168 ECB : */
412 andres 169 GIC 3 : if (log_opts.isatty || log_opts.verbose)
3775 bruce 170 UIC 0 : pg_log(PG_REPORT, "%-*s", MESSAGE_WIDTH, message);
171 : else
271 tgl 172 GNC 3 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
4715 bruce 173 CBC 3 : }
4715 bruce 174 ECB :
2936 tgl 175 : static void
3477 peter_e 176 GIC 3609 : 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. */
271 tgl 181 GNC 3609 : Assert(fmt);
182 3609 : Assert(fmt[0] == '\0' || fmt[strlen(fmt) - 1] != '\n');
183 :
2344 peter_e 184 GIC 3609 : vsnprintf(message, sizeof(message), _(fmt), ap);
4715 bruce 185 ECB :
3775 bruce 186 EUB : /* PG_VERBOSE and PG_STATUS are only output in verbose mode */
187 : /* fopen() on log_opts.internal might have failed, so check it */
3775 bruce 188 CBC 3609 : if (((type != PG_VERBOSE && type != PG_STATUS) || log_opts.verbose) &&
189 97 : log_opts.internal != NULL)
190 : {
3775 bruce 191 GIC 97 : if (type == PG_STATUS)
192 : /* status messages get two leading spaces, see below */
3775 bruce 193 UIC 0 : fprintf(log_opts.internal, " %s\n", message);
271 tgl 194 GNC 97 : else if (type == PG_REPORT_NONL)
3775 bruce 195 GIC 44 : fprintf(log_opts.internal, "%s", message);
196 : else
271 tgl 197 GNC 53 : fprintf(log_opts.internal, "%s\n", message);
4045 bruce 198 GIC 97 : fflush(log_opts.internal);
4715 bruce 199 ECB : }
200 :
4715 bruce 201 GIC 3609 : switch (type)
4715 bruce 202 ECB : {
4045 bruce 203 GIC 1939 : case PG_VERBOSE:
4554 204 1939 : if (log_opts.verbose)
271 tgl 205 UNC 0 : printf("%s\n", message);
4715 bruce 206 CBC 1939 : break;
4715 bruce 207 ECB :
3775 bruce 208 GIC 1573 : case PG_STATUS:
332 tgl 209 ECB :
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).
412 andres 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 : */
412 andres 220 GIC 1573 : if (log_opts.isatty)
221 : {
271 tgl 222 UNC 0 : bool itfits = (strlen(message) <= MESSAGE_WIDTH - 2);
223 :
3602 bruce 224 ECB : /* prefix with "..." if we do leading truncation */
271 tgl 225 UNC 0 : printf(" %s%-*.*s\r",
226 : itfits ? "" : "...",
3602 bruce 227 EUB : MESSAGE_WIDTH - 2, MESSAGE_WIDTH - 2,
228 : itfits ? message :
3602 bruce 229 ECB : message + strlen(message) - MESSAGE_WIDTH + 3 + 2);
230 : }
412 andres 231 GIC 1573 : else if (log_opts.verbose)
2344 peter_e 232 UIC 0 : printf(" %s\n", message);
3775 bruce 233 GIC 1573 : break;
234 :
271 tgl 235 GNC 44 : case PG_REPORT_NONL:
236 : /* This option is for use by prep_status and friends */
271 tgl 237 GIC 44 : printf("%s", message);
238 44 : break;
239 :
4715 bruce 240 GNC 52 : case PG_REPORT:
241 : case PG_WARNING:
271 tgl 242 52 : printf("%s\n", message);
4715 bruce 243 52 : break;
244 :
4715 bruce 245 GIC 1 : case PG_FATAL:
246 : /* Extra newline in case we're interrupting status output */
271 tgl 247 GNC 1 : printf("\n%s\n", message);
2368 peter_e 248 CBC 1 : printf(_("Failure, exiting\n"));
3378 peter_e 249 GIC 1 : exit(1);
4715 bruce 250 EUB : break;
251 :
252 : /* No default:, we want a warning for omitted cases */
253 : }
4715 bruce 254 GIC 3608 : fflush(stdout);
255 3608 : }
256 :
257 :
3477 peter_e 258 ECB : void
3477 peter_e 259 GBC 3564 : pg_log(eLogType type, const char *fmt,...)
3477 peter_e 260 ECB : {
261 : va_list args;
262 :
3477 peter_e 263 GIC 3564 : va_start(args, fmt);
3477 peter_e 264 CBC 3564 : pg_log_v(type, fmt, args);
265 3564 : va_end(args);
3477 peter_e 266 GIC 3564 : }
3477 peter_e 267 ECB :
268 :
269 : void
3477 peter_e 270 LBC 0 : pg_fatal(const char *fmt,...)
271 : {
3477 peter_e 272 ECB : va_list args;
273 :
3477 peter_e 274 LBC 0 : va_start(args, fmt);
275 0 : pg_log_v(PG_FATAL, fmt, args);
276 0 : va_end(args);
277 : /* NOTREACHED */
2368 peter_e 278 UIC 0 : printf(_("Failure, exiting\n"));
3477 279 0 : exit(1);
280 : }
281 :
3477 peter_e 282 ECB :
4715 bruce 283 : void
4555 bruce 284 GIC 44 : check_ok(void)
285 : {
286 : /* all seems well */
4555 bruce 287 CBC 44 : report_status(PG_REPORT, "ok");
4715 bruce 288 GIC 44 : }
289 :
4715 bruce 290 ECB :
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.
4715 bruce 297 EUB : */
298 : char *
4555 bruce 299 GIC 2 : quote_identifier(const char *s)
300 : {
4555 bruce 301 GBC 2 : char *result = pg_malloc(strlen(s) * 2 + 3);
4715 302 2 : char *r = result;
4715 bruce 303 EUB :
4715 bruce 304 GIC 2 : *r++ = '"';
4715 bruce 305 GBC 20 : while (*s)
4715 bruce 306 EUB : {
4715 bruce 307 GIC 18 : if (*s == '"')
4715 bruce 308 UIC 0 : *r++ = *s;
4715 bruce 309 GIC 18 : *r++ = *s;
310 18 : s++;
4715 bruce 311 ECB : }
4715 bruce 312 GIC 2 : *r++ = '"';
313 2 : *r++ = '\0';
4715 bruce 314 ECB :
4715 bruce 315 CBC 2 : return result;
316 : }
317 :
318 :
319 : /*
320 : * get_user_info()
321 : */
322 : int
3399 rhaas 323 GIC 6 : get_user_info(char **user_name_p)
324 : {
325 : int user_id;
3399 rhaas 326 ECB : const char *user_name;
327 : char *errstr;
4660 bruce 328 :
4715 329 : #ifndef WIN32
4715 bruce 330 GIC 6 : user_id = geteuid();
3399 bruce 331 ECB : #else
4715 332 : user_id = 1;
333 : #endif
334 :
3399 rhaas 335 GBC 6 : user_name = get_user_name(&errstr);
3399 rhaas 336 CBC 6 : if (!user_name)
271 tgl 337 UNC 0 : pg_fatal("%s", errstr);
338 :
3399 bruce 339 ECB : /* make a copy */
3399 rhaas 340 CBC 6 : *user_name_p = pg_strdup(user_name);
341 :
4715 bruce 342 6 : return user_id;
343 : }
344 :
345 :
346 : /*
347 : * str2uint()
348 : *
349 : * convert string to oid
4576 bruce 350 ECB : */
351 : unsigned int
4576 bruce 352 GIC 80 : str2uint(const char *str)
353 : {
4575 354 80 : return strtoul(str, NULL, 10);
355 : }
|