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