1 /*
2 * ****************************************************************************
3 * Copyright (c) 2013-2019, PyInstaller Development Team.
4 * Distributed under the terms of the GNU General Public License with exception
5 * for distributing bootloader.
6 *
7 * The full license is in the file COPYING.txt, distributed with this software.
8 * ****************************************************************************
9 */
10
11 /*
12 * Global shared fuctions used in many bootloader files.
13 */
14
15 /*
16 * Enable use of Sean's Tool Box -- public domain -- http://nothings.org/stb.h.
17 * File stb.h.
18 * All functions starting with 'stb_' prefix are from this toolbox.
19 *
20 * This define has to be only in one C source file!
21 */
22 /* #define STB_DEFINE 1/ * * / */
23 /* #define STB_NO_REGISTRY 1 / * No need for Windows registry functions in stb.h. * / */
24
25 /* TODO: use safe string functions */
26 #define _CRT_SECURE_NO_WARNINGS 1
27
28 #include <stdarg.h> /* va_list, va_start(), va_end() */
29 #include <stdio.h>
30
31 #ifdef _WIN32
32 #include <windows.h>
33 #include <direct.h>
34 #include <process.h>
35 #include <io.h>
36 #else
37 #include <sys/types.h>
38 #include <unistd.h>
39 #endif
40
41 /* On Mac OS X send debug msg also to syslog for gui app in debug mode. */
42 #if defined(__APPLE__) && defined(WINDOWED) && defined(LAUNCH_DEBUG)
43 #include <syslog.h>
44 #endif
45
46 /* PyInstaller headers. */
47 #include "pyi_global.h"
48 #include "pyi_win32_utils.h"
49 /* Text length of MessageBox(). */
50 #define MBTXTLEN 1024
51
52 /* Locale is saved at the start of main(), and restored immediately before running
53 * scripts in pyi_launch_run_scripts
54 */
55 char *saved_locale;
56
57 /*
58 * On Windows and with windowed mode (no console) show error messages
59 * in message boxes. In windowed mode nothing is written to console.
60 */
61
62 #if defined(_WIN32) && defined(WINDOWED)
63 void
show_message_box(const char * msg,const char * caption,UINT uType)64 show_message_box(const char *msg, const char *caption, UINT uType)
65 {
66 wchar_t wmsg[MBTXTLEN];
67 wchar_t wcaption[MBTXTLEN] = L"";
68 if (pyi_win32_utils_from_utf8(wmsg, msg, MBTXTLEN)) {
69 /* converting the caption is expected to pass since the given caption
70 * is always written in US-ASCII and hard-coded, currently.
71 */
72 pyi_win32_utils_from_utf8(wcaption, caption, MBTXTLEN);
73 MessageBoxW(NULL, wmsg, wcaption, MB_OK | uType);
74 }
75 else {
76 /* The msg here is always shown as not human-readable string,
77 * but can be the hint what the real message is.
78 */
79 MessageBoxA(NULL, msg, caption, MB_OK | uType);
80 }
81 }
82
83
84 void
mbfatalerror(const char * fmt,...)85 mbfatalerror(const char *fmt, ...)
86 {
87 char msg[MBTXTLEN];
88 va_list args;
89
90 va_start(args, fmt);
91 vsnprintf(msg, MBTXTLEN, fmt, args);
92 va_end(args);
93
94 show_message_box(msg, "Fatal error detected", MB_ICONEXCLAMATION);
95 }
96
97 void
mbothererror(const char * fmt,...)98 mbothererror(const char *fmt, ...)
99 {
100 char msg[MBTXTLEN];
101 va_list args;
102
103 va_start(args, fmt);
104 vsnprintf(msg, MBTXTLEN, fmt, args);
105 va_end(args);
106
107 show_message_box(msg, "Error detected", MB_ICONWARNING);
108 }
109
mbfatal_winerror(const char * funcname,const char * fmt,...)110 void mbfatal_winerror(const char * funcname, const char *fmt, ...)
111 {
112 char msg[MBTXTLEN];
113 int size = 0;
114 DWORD error_code = GetLastError();
115 va_list args;
116
117 va_start(args, fmt);
118 size = vsnprintf(msg, MBTXTLEN, fmt, args);
119 va_end(args);
120
121 if(size < MBTXTLEN) {
122 strncpy(msg + size, funcname, MBTXTLEN - size - 1);
123 size += strlen(funcname);
124 }
125
126 if(size < MBTXTLEN) {
127 strncpy(msg + size, ": ", 2);
128 size += 2;
129 }
130
131 if(size < MBTXTLEN) {
132 strncpy(msg + size, GetWinErrorString(error_code), MBTXTLEN - size - 1);
133 }
134
135 msg[MBTXTLEN-1] = '\0';
136
137 show_message_box(msg, "Fatal error detected", MB_ICONEXCLAMATION);
138 }
139
mbfatal_perror(const char * funcname,const char * fmt,...)140 void mbfatal_perror(const char * funcname, const char *fmt, ...)
141 {
142 char msg[MBTXTLEN];
143 int size = 0;
144 va_list args;
145
146 va_start(args, fmt);
147 size = vsnprintf(msg, MBTXTLEN, fmt, args);
148 va_end(args);
149
150 if(size < MBTXTLEN) {
151 strncpy(msg + size, funcname, MBTXTLEN - size - 1);
152 size += strlen(funcname);
153 }
154
155 if(size < MBTXTLEN) {
156 strncpy(msg + size, ": ", 2);
157 size += 2;
158 }
159
160 if(size < MBTXTLEN) {
161 strncpy(msg + size, strerror(errno), MBTXTLEN - size - 1);
162 }
163
164 msg[MBTXTLEN-1] = '\0';
165
166 show_message_box(msg, "Fatal error detected", MB_ICONEXCLAMATION);
167 }
168 #endif /* _WIN32 and WINDOWED */
169
170 /* Enable or disable debug output. */
171
172 #ifdef LAUNCH_DEBUG
173 #if defined(_WIN32) && defined(WINDOWED)
174 void
mbvs(const char * fmt,...)175 mbvs(const char *fmt, ...)
176 {
177 char msg[MBTXTLEN];
178 va_list args;
179 int pid_len;
180
181 /* Add pid to the message */
182 pid_len = sprintf(msg, "[%d] ", getpid());
183
184 va_start(args, fmt);
185 vsnprintf(&msg[pid_len], MBTXTLEN-pid_len, fmt, args);
186 /* Ensure message is trimmed to fit the buffer. */
187 /* msg[MBTXTLEN-1] = '\0'; */
188 va_end(args);
189
190 show_message_box(msg, "Tracing...", MB_ICONINFORMATION);
191 }
192 #endif /* if defined(_WIN32) && defined(WINDOWED) */
193 #endif /* ifdef LAUNCH_DEBUG */
194
195 #define VPRINTF_TO_STDERR_BUFSIZE (MBTXTLEN * 2)
vprintf_to_stderr(const char * fmt,va_list v)196 void vprintf_to_stderr(const char *fmt, va_list v) {
197 #if defined(_WIN32)
198 char utf8_buffer[VPRINTF_TO_STDERR_BUFSIZE];
199 char mbcs_buffer[VPRINTF_TO_STDERR_BUFSIZE];
200
201 vsnprintf(utf8_buffer, VPRINTF_TO_STDERR_BUFSIZE, fmt, v);
202 if (pyi_win32_utf8_to_mbs(mbcs_buffer,
203 utf8_buffer,
204 VPRINTF_TO_STDERR_BUFSIZE)) {
205 fprintf(stderr, "%s", mbcs_buffer);
206 }
207 else {
208 fprintf(stderr, "%s", utf8_buffer);
209 }
210 #else
211 vfprintf(stderr, fmt, v);
212 #endif /* if defined(_WIN32) */
213 }
214
printf_to_stderr(const char * fmt,...)215 void printf_to_stderr(const char* fmt, ...) {
216 va_list v;
217 va_start(v, fmt);
218 vprintf_to_stderr(fmt, v);
219 va_end(v);
220 }
221
222 /*
223 * Wrap printing debug messages to console.
224 */
225 void
pyi_global_printf(const char * fmt,...)226 pyi_global_printf(const char *fmt, ...)
227 {
228 va_list v;
229
230 /* Sent 'LOADER text' messages to stderr. */
231 fprintf(stderr, "[%d] ", getpid());
232 va_start(v, fmt);
233 vprintf_to_stderr(fmt, v);
234 va_end(v);
235 /* For Gui apps on Mac OS X send debug messages also to syslog. */
236 /* This allows to see bootloader debug messages in the Console.app log viewer. */
237 /* https://en.wikipedia.org/wiki/Console_(OS_X) */
238 /* Levels DEBUG and INFO are ignored so use level NOTICE. */
239 #if defined(__APPLE__) && defined(WINDOWED) && defined(LAUNCH_DEBUG)
240 va_start(v, fmt);
241 vsyslog(LOG_NOTICE, fmt, v);
242 va_end(v);
243 #endif
244 }
245
246 /*
247 * Print a debug message followed by the name of the function that resulted in an error
248 * and a textual description of the error, as with perror().
249 */
pyi_global_perror(const char * funcname,const char * fmt,...)250 void pyi_global_perror(const char *funcname, const char *fmt, ...) {
251 va_list v;
252
253 va_start(v, fmt);
254 vprintf_to_stderr(fmt, v);
255 va_end(v);
256 perror(funcname); // perror() writes to stderr
257
258 #if defined(__APPLE__) && defined(WINDOWED) && defined(LAUNCH_DEBUG)
259 va_start(v, fmt);
260 vsyslog(LOG_NOTICE, fmt, v);
261 vsyslog(LOG_NOTICE, "%m\n", NULL); // %m emits the result of strerror()
262 va_end(v);
263 #endif
264 }
265
266 /*
267 * Windows errors.
268 *
269 * Print a debug message followed by the name of the function that resulted in an error
270 * and a textual description of the error, as returned by FormatMessage.
271 */
272 #ifdef _WIN32
pyi_global_winerror(const char * funcname,const char * fmt,...)273 void pyi_global_winerror(const char *funcname, const char *fmt, ...) {
274 va_list v;
275
276 va_start(v, fmt);
277 vprintf_to_stderr(fmt, v);
278 va_end(v);
279 printf_to_stderr("%s: %s", funcname, GetWinErrorString(GetLastError()));
280 }
281 #endif
282