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