1 /*
2  *	util.c
3  *
4  *	utility functions
5  *
6  *	Copyright (c) 2010-2018, PostgreSQL Global Development Group
7  *	src/bin/pg_upgrade/util.c
8  */
9 
10 #include "postgres_fe.h"
11 
12 #include "common/username.h"
13 #include "pg_upgrade.h"
14 
15 #include <signal.h>
16 
17 
18 LogOpts		log_opts;
19 
20 static void pg_log_v(eLogType type, const char *fmt, va_list ap) pg_attribute_printf(2, 0);
21 
22 
23 /*
24  * report_status()
25  *
26  *	Displays the result of an operation (ok, failed, error message,...)
27  */
28 void
report_status(eLogType type,const char * fmt,...)29 report_status(eLogType type, const char *fmt,...)
30 {
31 	va_list		args;
32 	char		message[MAX_STRING];
33 
34 	va_start(args, fmt);
35 	vsnprintf(message, sizeof(message), fmt, args);
36 	va_end(args);
37 
38 	pg_log(type, "%s\n", message);
39 }
40 
41 
42 /* force blank output for progress display */
43 void
end_progress_output(void)44 end_progress_output(void)
45 {
46 	/*
47 	 * In case nothing printed; pass a space so gcc doesn't complain about
48 	 * empty format string.
49 	 */
50 	prep_status(" ");
51 }
52 
53 
54 /*
55  * prep_status
56  *
57  *	Displays a message that describes an operation we are about to begin.
58  *	We pad the message out to MESSAGE_WIDTH characters so that all of the "ok" and
59  *	"failed" indicators line up nicely.
60  *
61  *	A typical sequence would look like this:
62  *		prep_status("about to flarb the next %d files", fileCount );
63  *
64  *		if(( message = flarbFiles(fileCount)) == NULL)
65  *		  report_status(PG_REPORT, "ok" );
66  *		else
67  *		  pg_log(PG_FATAL, "failed - %s\n", message );
68  */
69 void
prep_status(const char * fmt,...)70 prep_status(const char *fmt,...)
71 {
72 	va_list		args;
73 	char		message[MAX_STRING];
74 
75 	va_start(args, fmt);
76 	vsnprintf(message, sizeof(message), fmt, args);
77 	va_end(args);
78 
79 	if (strlen(message) > 0 && message[strlen(message) - 1] == '\n')
80 		pg_log(PG_REPORT, "%s", message);
81 	else
82 		/* trim strings that don't end in a newline */
83 		pg_log(PG_REPORT, "%-*s", MESSAGE_WIDTH, message);
84 }
85 
86 
87 static void
pg_log_v(eLogType type,const char * fmt,va_list ap)88 pg_log_v(eLogType type, const char *fmt, va_list ap)
89 {
90 	char		message[QUERY_ALLOC];
91 
92 	vsnprintf(message, sizeof(message), _(fmt), ap);
93 
94 	/* PG_VERBOSE and PG_STATUS are only output in verbose mode */
95 	/* fopen() on log_opts.internal might have failed, so check it */
96 	if (((type != PG_VERBOSE && type != PG_STATUS) || log_opts.verbose) &&
97 		log_opts.internal != NULL)
98 	{
99 		if (type == PG_STATUS)
100 			/* status messages need two leading spaces and a newline */
101 			fprintf(log_opts.internal, "  %s\n", message);
102 		else
103 			fprintf(log_opts.internal, "%s", message);
104 		fflush(log_opts.internal);
105 	}
106 
107 	switch (type)
108 	{
109 		case PG_VERBOSE:
110 			if (log_opts.verbose)
111 				printf("%s", message);
112 			break;
113 
114 		case PG_STATUS:
115 			/* for output to a display, do leading truncation and append \r */
116 			if (isatty(fileno(stdout)))
117 				/* -2 because we use a 2-space indent */
118 				printf("  %s%-*.*s\r",
119 				/* prefix with "..." if we do leading truncation */
120 					   strlen(message) <= MESSAGE_WIDTH - 2 ? "" : "...",
121 					   MESSAGE_WIDTH - 2, MESSAGE_WIDTH - 2,
122 				/* optional leading truncation */
123 					   strlen(message) <= MESSAGE_WIDTH - 2 ? message :
124 					   message + strlen(message) - MESSAGE_WIDTH + 3 + 2);
125 			else
126 				printf("  %s\n", message);
127 			break;
128 
129 		case PG_REPORT:
130 		case PG_WARNING:
131 			printf("%s", message);
132 			break;
133 
134 		case PG_FATAL:
135 			printf("\n%s", message);
136 			printf(_("Failure, exiting\n"));
137 			exit(1);
138 			break;
139 
140 		default:
141 			break;
142 	}
143 	fflush(stdout);
144 }
145 
146 
147 void
pg_log(eLogType type,const char * fmt,...)148 pg_log(eLogType type, const char *fmt,...)
149 {
150 	va_list		args;
151 
152 	va_start(args, fmt);
153 	pg_log_v(type, fmt, args);
154 	va_end(args);
155 }
156 
157 
158 void
pg_fatal(const char * fmt,...)159 pg_fatal(const char *fmt,...)
160 {
161 	va_list		args;
162 
163 	va_start(args, fmt);
164 	pg_log_v(PG_FATAL, fmt, args);
165 	va_end(args);
166 	printf(_("Failure, exiting\n"));
167 	exit(1);
168 }
169 
170 
171 void
check_ok(void)172 check_ok(void)
173 {
174 	/* all seems well */
175 	report_status(PG_REPORT, "ok");
176 	fflush(stdout);
177 }
178 
179 
180 /*
181  * quote_identifier()
182  *		Properly double-quote a SQL identifier.
183  *
184  * The result should be pg_free'd, but most callers don't bother because
185  * memory leakage is not a big deal in this program.
186  */
187 char *
quote_identifier(const char * s)188 quote_identifier(const char *s)
189 {
190 	char	   *result = pg_malloc(strlen(s) * 2 + 3);
191 	char	   *r = result;
192 
193 	*r++ = '"';
194 	while (*s)
195 	{
196 		if (*s == '"')
197 			*r++ = *s;
198 		*r++ = *s;
199 		s++;
200 	}
201 	*r++ = '"';
202 	*r++ = '\0';
203 
204 	return result;
205 }
206 
207 
208 /*
209  * get_user_info()
210  */
211 int
get_user_info(char ** user_name_p)212 get_user_info(char **user_name_p)
213 {
214 	int			user_id;
215 	const char *user_name;
216 	char	   *errstr;
217 
218 #ifndef WIN32
219 	user_id = geteuid();
220 #else
221 	user_id = 1;
222 #endif
223 
224 	user_name = get_user_name(&errstr);
225 	if (!user_name)
226 		pg_fatal("%s\n", errstr);
227 
228 	/* make a copy */
229 	*user_name_p = pg_strdup(user_name);
230 
231 	return user_id;
232 }
233 
234 
235 /*
236  *	str2uint()
237  *
238  *	convert string to oid
239  */
240 unsigned int
str2uint(const char * str)241 str2uint(const char *str)
242 {
243 	return strtoul(str, NULL, 10);
244 }
245 
246 
247 /*
248  *	pg_putenv()
249  *
250  *	This is like putenv(), but takes two arguments.
251  *	It also does unsetenv() if val is NULL.
252  */
253 void
pg_putenv(const char * var,const char * val)254 pg_putenv(const char *var, const char *val)
255 {
256 	if (val)
257 	{
258 #ifndef WIN32
259 		char	   *envstr;
260 
261 		envstr = psprintf("%s=%s", var, val);
262 		putenv(envstr);
263 
264 		/*
265 		 * Do not free envstr because it becomes part of the environment on
266 		 * some operating systems.  See port/unsetenv.c::unsetenv.
267 		 */
268 #else
269 		SetEnvironmentVariableA(var, val);
270 #endif
271 	}
272 	else
273 	{
274 #ifndef WIN32
275 		unsetenv(var);
276 #else
277 		SetEnvironmentVariableA(var, "");
278 #endif
279 	}
280 }
281