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
32 312 : report_status(eLogType type, const char *fmt,...)
33 : {
34 : va_list args;
35 :
36 312 : va_start(args, fmt);
37 312 : pg_log_v(type, fmt, args);
38 310 : va_end(args);
39 310 : }
40 :
41 :
42 : void
43 22 : 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 : */
50 22 : if (log_opts.isatty)
51 : {
52 0 : printf("\r");
53 0 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, "");
54 : }
55 22 : else if (log_opts.verbose)
56 0 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, "");
57 22 : }
58 :
59 : /*
60 : * Remove any logs generated internally. To be used once when exiting.
61 : */
62 : void
63 8 : cleanup_output_dirs(void)
64 : {
65 8 : fclose(log_opts.internal);
66 :
67 : /* Remove dump and log files? */
68 8 : if (log_opts.retain)
69 0 : return;
70 :
71 : /*
72 : * Try twice. The second time might wait for files to finish being
73 : * unlinked, on Windows.
74 : */
75 8 : if (!rmtree(log_opts.basedir, true))
76 0 : rmtree(log_opts.basedir, true);
77 :
78 : /* Remove pg_upgrade_output.d only if empty */
79 8 : switch (pg_check_dir(log_opts.rootdir))
80 : {
81 0 : case 0: /* non-existent */
82 : case 3: /* exists and contains a mount point */
83 : Assert(false);
84 0 : break;
85 :
86 6 : 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 6 : if (!rmtree(log_opts.rootdir, true))
94 0 : rmtree(log_opts.rootdir, true);
95 6 : 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 :
105 0 : default:
106 : /* different failure, just report it */
107 0 : pg_log(PG_WARNING, "could not access directory \"%s\": %m",
108 : log_opts.rootdir);
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
129 298 : prep_status(const char *fmt,...)
130 : {
131 : va_list args;
132 : char message[MAX_STRING];
133 :
134 298 : va_start(args, fmt);
135 298 : vsnprintf(message, sizeof(message), fmt, args);
136 298 : va_end(args);
137 :
138 : /* trim strings */
139 298 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
140 298 : }
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 22 : prep_status_progress(const char *fmt,...)
157 : {
158 : va_list args;
159 : char message[MAX_STRING];
160 :
161 22 : va_start(args, fmt);
162 22 : vsnprintf(message, sizeof(message), fmt, args);
163 22 : 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 22 : if (log_opts.isatty || log_opts.verbose)
170 0 : pg_log(PG_REPORT, "%-*s", MESSAGE_WIDTH, message);
171 : else
172 22 : pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
173 22 : }
174 :
175 : static void
176 9410 : 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 9410 : 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 9410 : if (((type != PG_VERBOSE && type != PG_STATUS) || log_opts.verbose) &&
189 698 : log_opts.internal != NULL)
190 : {
191 698 : if (type == PG_STATUS)
192 : /* status messages get two leading spaces, see below */
193 0 : fprintf(log_opts.internal, " %s\n", message);
194 698 : else if (type == PG_REPORT_NONL)
195 320 : fprintf(log_opts.internal, "%s", message);
196 : else
197 378 : fprintf(log_opts.internal, "%s\n", message);
198 698 : fflush(log_opts.internal);
199 : }
200 :
201 9410 : switch (type)
202 : {
203 5364 : case PG_VERBOSE:
204 5364 : if (log_opts.verbose)
205 0 : printf("%s\n", message);
206 5364 : break;
207 :
208 3348 : 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 3348 : if (log_opts.isatty)
221 : {
222 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 : }
231 3348 : else if (log_opts.verbose)
232 0 : printf(" %s\n", message);
233 3348 : break;
234 :
235 320 : case PG_REPORT_NONL:
236 : /* This option is for use by prep_status and friends */
237 320 : printf("%s", message);
238 320 : break;
239 :
240 366 : case PG_REPORT:
241 : case PG_WARNING:
242 366 : printf("%s\n", message);
243 366 : break;
244 :
245 12 : case PG_FATAL:
246 : /* Extra newline in case we're interrupting status output */
247 12 : printf("\n%s\n", message);
248 12 : printf(_("Failure, exiting\n"));
249 12 : exit(1);
250 : break;
251 :
252 : /* No default:, we want a warning for omitted cases */
253 : }
254 9398 : fflush(stdout);
255 9398 : }
256 :
257 :
258 : void
259 9088 : pg_log(eLogType type, const char *fmt,...)
260 : {
261 : va_list args;
262 :
263 9088 : va_start(args, fmt);
264 9088 : pg_log_v(type, fmt, args);
265 9088 : va_end(args);
266 9088 : }
267 :
268 :
269 : void
270 10 : pg_fatal(const char *fmt,...)
271 : {
272 : va_list args;
273 :
274 10 : va_start(args, fmt);
275 10 : pg_log_v(PG_FATAL, fmt, args);
276 0 : va_end(args);
277 : /* NOTREACHED */
278 0 : printf(_("Failure, exiting\n"));
279 0 : exit(1);
280 : }
281 :
282 :
283 : void
284 310 : check_ok(void)
285 : {
286 : /* all seems well */
287 310 : report_status(PG_REPORT, "ok");
288 310 : }
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 12 : quote_identifier(const char *s)
300 : {
301 12 : char *result = pg_malloc(strlen(s) * 2 + 3);
302 12 : char *r = result;
303 :
304 12 : *r++ = '"';
305 120 : while (*s)
306 : {
307 108 : if (*s == '"')
308 0 : *r++ = *s;
309 108 : *r++ = *s;
310 108 : s++;
311 : }
312 12 : *r++ = '"';
313 12 : *r++ = '\0';
314 :
315 12 : return result;
316 : }
317 :
318 :
319 : /*
320 : * get_user_info()
321 : */
322 : int
323 26 : 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 26 : user_id = geteuid();
331 : #else
332 : user_id = 1;
333 : #endif
334 :
335 26 : user_name = get_user_name(&errstr);
336 26 : if (!user_name)
337 0 : pg_fatal("%s", errstr);
338 :
339 : /* make a copy */
340 26 : *user_name_p = pg_strdup(user_name);
341 :
342 26 : return user_id;
343 : }
344 :
345 :
346 : /*
347 : * str2uint()
348 : *
349 : * convert string to oid
350 : */
351 : unsigned int
352 720 : str2uint(const char *str)
353 : {
354 720 : return strtoul(str, NULL, 10);
355 : }
|