1 /* Any copyright is dedicated to the Public Domain.
2 * http://creativecommons.org/publicdomain/zero/1.0/
3 */
4
5 #include "updatedefines.h"
6
7 #ifdef XP_WIN
8 # include "commonupdatedir.h"
9 # include "updatehelper.h"
10 # include "certificatecheck.h"
11 # define NS_main wmain
12 # define NS_tgetcwd _wgetcwd
13 # define NS_ttoi _wtoi
14 #else
15 # define NS_main main
16 # define NS_tgetcwd getcwd
17 # define NS_ttoi atoi
18 #endif
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26
WriteMsg(const NS_tchar * path,const char * status)27 static void WriteMsg(const NS_tchar* path, const char* status) {
28 FILE* outFP = NS_tfopen(path, NS_T("wb"));
29 if (!outFP) {
30 return;
31 }
32
33 fprintf(outFP, "%s\n", status);
34 fclose(outFP);
35 outFP = nullptr;
36 }
37
CheckMsg(const NS_tchar * path,const char * expected)38 static bool CheckMsg(const NS_tchar* path, const char* expected) {
39 FILE* inFP = NS_tfopen(path, NS_T("rb"));
40 if (!inFP) {
41 return false;
42 }
43
44 struct stat ms;
45 if (fstat(fileno(inFP), &ms)) {
46 fclose(inFP);
47 inFP = nullptr;
48 return false;
49 }
50
51 char* mbuf = (char*)malloc(ms.st_size + 1);
52 if (!mbuf) {
53 fclose(inFP);
54 inFP = nullptr;
55 return false;
56 }
57
58 size_t r = ms.st_size;
59 char* rb = mbuf;
60 size_t c = fread(rb, sizeof(char), 50, inFP);
61 r -= c;
62 if (c == 0 && r) {
63 free(mbuf);
64 fclose(inFP);
65 inFP = nullptr;
66 return false;
67 }
68 mbuf[ms.st_size] = '\0';
69 rb = mbuf;
70
71 bool isMatch = strcmp(rb, expected) == 0;
72 free(mbuf);
73 fclose(inFP);
74 inFP = nullptr;
75 return isMatch;
76 }
77
NS_main(int argc,NS_tchar ** argv)78 int NS_main(int argc, NS_tchar** argv) {
79 if (argc == 2) {
80 if (!NS_tstrcmp(argv[1], NS_T("post-update-async")) ||
81 !NS_tstrcmp(argv[1], NS_T("post-update-sync"))) {
82 NS_tchar exePath[MAXPATHLEN];
83 #ifdef XP_WIN
84 if (!::GetModuleFileNameW(0, exePath, MAXPATHLEN)) {
85 return 1;
86 }
87 #else
88 if (!NS_tvsnprintf(exePath, sizeof(exePath) / sizeof(exePath[0]),
89 NS_T("%s"), argv[0])) {
90 return 1;
91 }
92 #endif
93 NS_tchar runFilePath[MAXPATHLEN];
94 if (!NS_tvsnprintf(runFilePath,
95 sizeof(runFilePath) / sizeof(runFilePath[0]),
96 NS_T("%s.running"), exePath)) {
97 return 1;
98 }
99 #ifdef XP_WIN
100 if (!NS_taccess(runFilePath, F_OK)) {
101 // This makes it possible to check if the post update process was
102 // launched twice which happens when the service performs an update.
103 NS_tchar runFilePathBak[MAXPATHLEN];
104 if (!NS_tvsnprintf(runFilePathBak,
105 sizeof(runFilePathBak) / sizeof(runFilePathBak[0]),
106 NS_T("%s.bak"), runFilePath)) {
107 return 1;
108 }
109 MoveFileExW(runFilePath, runFilePathBak, MOVEFILE_REPLACE_EXISTING);
110 }
111 #endif
112 WriteMsg(runFilePath, "running");
113
114 if (!NS_tstrcmp(argv[1], NS_T("post-update-sync"))) {
115 #ifdef XP_WIN
116 Sleep(2000);
117 #else
118 sleep(2);
119 #endif
120 }
121
122 NS_tchar logFilePath[MAXPATHLEN];
123 if (!NS_tvsnprintf(logFilePath,
124 sizeof(logFilePath) / sizeof(logFilePath[0]),
125 NS_T("%s.log"), exePath)) {
126 return 1;
127 }
128 WriteMsg(logFilePath, "post-update");
129 return 0;
130 }
131 }
132
133 if (argc < 3) {
134 fprintf(
135 stderr,
136 "\n"
137 "Application Update Service Test Helper\n"
138 "\n"
139 "Usage: WORKINGDIR INFILE OUTFILE -s SECONDS [FILETOLOCK]\n"
140 " or: WORKINGDIR LOGFILE [ARG2 ARG3...]\n"
141 " or: signature-check filepath\n"
142 " or: setup-symlink dir1 dir2 file symlink\n"
143 " or: remove-symlink dir1 dir2 file symlink\n"
144 " or: check-symlink symlink\n"
145 " or: check-umask existing-umask\n"
146 " or: post-update\n"
147 " or: create-update-dir\n"
148 "\n"
149 " WORKINGDIR \tThe relative path to the working directory to use.\n"
150 " INFILE \tThe relative path from the working directory for the "
151 "file to\n"
152 " \tread actions to perform such as finish.\n"
153 " OUTFILE \tThe relative path from the working directory for the "
154 "file to\n"
155 " \twrite status information.\n"
156 " SECONDS \tThe number of seconds to sleep.\n"
157 " FILETOLOCK \tThe relative path from the working directory to an "
158 "existing\n"
159 " \tfile to open exlusively.\n"
160 " \tOnly available on Windows platforms and silently "
161 "ignored on\n"
162 " \tother platforms.\n"
163 " LOGFILE \tThe relative path from the working directory to log "
164 "the\n"
165 " \tcommand line arguments.\n"
166 " ARG2 ARG3...\tArguments to write to the LOGFILE after the preceding "
167 "command\n"
168 " \tline arguments.\n"
169 "\n"
170 "Note: All paths must be relative.\n"
171 "\n");
172 return 1;
173 }
174
175 if (!NS_tstrcmp(argv[1], NS_T("check-signature"))) {
176 #if defined(XP_WIN) && defined(MOZ_MAINTENANCE_SERVICE)
177 if (ERROR_SUCCESS == VerifyCertificateTrustForFile(argv[2])) {
178 return 0;
179 } else {
180 return 1;
181 }
182 #else
183 // Not implemented on non-Windows platforms
184 return 1;
185 #endif
186 }
187
188 if (!NS_tstrcmp(argv[1], NS_T("setup-symlink"))) {
189 #ifdef XP_UNIX
190 NS_tchar path[MAXPATHLEN];
191 if (!NS_tvsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
192 NS_T("/tmp"), argv[2])) {
193 return 1;
194 }
195 if (mkdir(path, 0755)) {
196 return 1;
197 }
198 if (!NS_tvsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s/%s"),
199 NS_T("/tmp"), argv[2], argv[3])) {
200 return 1;
201 }
202 if (mkdir(path, 0755)) {
203 return 1;
204 }
205 if (!NS_tvsnprintf(path, sizeof(path) / sizeof(path[0]),
206 NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3],
207 argv[4])) {
208 return 1;
209 }
210 FILE* file = NS_tfopen(path, NS_T("w"));
211 if (file) {
212 fputs(NS_T("test"), file);
213 fclose(file);
214 }
215 if (symlink(path, argv[5]) != 0) {
216 return 1;
217 }
218 if (!NS_tvsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
219 NS_T("/tmp"), argv[2])) {
220 return 1;
221 }
222 if (argc > 6 && !NS_tstrcmp(argv[6], NS_T("change-perm"))) {
223 if (chmod(path, 0644)) {
224 return 1;
225 }
226 }
227 return 0;
228 #else
229 // Not implemented on non-Unix platforms
230 return 1;
231 #endif
232 }
233
234 if (!NS_tstrcmp(argv[1], NS_T("remove-symlink"))) {
235 #ifdef XP_UNIX
236 // The following can be called at the start of a test in case these symlinks
237 // need to be removed if they already exist and at the end of a test to
238 // remove the symlinks created by the test so ignore file doesn't exist
239 // errors.
240 NS_tchar path[MAXPATHLEN];
241 if (!NS_tvsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
242 NS_T("/tmp"), argv[2])) {
243 return 1;
244 }
245 if (chmod(path, 0755) && errno != ENOENT) {
246 return 1;
247 }
248 if (!NS_tvsnprintf(path, sizeof(path) / sizeof(path[0]),
249 NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3],
250 argv[4])) {
251 return 1;
252 }
253 if (unlink(path) && errno != ENOENT) {
254 return 1;
255 }
256 if (!NS_tvsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s/%s"),
257 NS_T("/tmp"), argv[2], argv[3])) {
258 return 1;
259 }
260 if (rmdir(path) && errno != ENOENT) {
261 return 1;
262 }
263 if (!NS_tvsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
264 NS_T("/tmp"), argv[2])) {
265 return 1;
266 }
267 if (rmdir(path) && errno != ENOENT) {
268 return 1;
269 }
270 return 0;
271 #else
272 // Not implemented on non-Unix platforms
273 return 1;
274 #endif
275 }
276
277 if (!NS_tstrcmp(argv[1], NS_T("check-symlink"))) {
278 #ifdef XP_UNIX
279 struct stat ss;
280 if (lstat(argv[2], &ss)) {
281 return 1;
282 }
283 return S_ISLNK(ss.st_mode) ? 0 : 1;
284 #else
285 // Not implemented on non-Unix platforms
286 return 1;
287 #endif
288 }
289
290 if (!NS_tstrcmp(argv[1], NS_T("check-umask"))) {
291 #ifdef XP_UNIX
292 // Discover the current value of the umask. There is no way to read the
293 // umask without changing it. The system call is specified as unable to
294 // fail.
295 uint32_t umask = ::umask(0777);
296 ::umask(umask);
297
298 NS_tchar logFilePath[MAXPATHLEN];
299 if (!NS_tvsnprintf(logFilePath,
300 sizeof(logFilePath) / sizeof(logFilePath[0]), NS_T("%s"),
301 argv[2])) {
302 return 1;
303 }
304
305 FILE* logFP = NS_tfopen(logFilePath, NS_T("wb"));
306 if (!logFP) {
307 return 1;
308 }
309 fprintf(logFP, "check-umask\numask-%d\n", umask);
310
311 fclose(logFP);
312 logFP = nullptr;
313
314 return 0;
315 #else
316 // Not implemented on non-Unix platforms
317 return 1;
318 #endif
319 }
320
321 if (!NS_tstrcmp(argv[1], NS_T("wait-for-service-stop"))) {
322 #if defined(XP_WIN) && defined(MOZ_MAINTENANCE_SERVICE)
323 const int maxWaitSeconds = NS_ttoi(argv[3]);
324 LPCWSTR serviceName = argv[2];
325 DWORD serviceState = WaitForServiceStop(serviceName, maxWaitSeconds);
326 if (SERVICE_STOPPED == serviceState) {
327 return 0;
328 } else {
329 return serviceState;
330 }
331 #else
332 // Not implemented on non-Windows platforms
333 return 1;
334 #endif
335 }
336
337 if (!NS_tstrcmp(argv[1], NS_T("wait-for-application-exit"))) {
338 #ifdef XP_WIN
339 const int maxWaitSeconds = NS_ttoi(argv[3]);
340 LPCWSTR application = argv[2];
341 DWORD ret = WaitForProcessExit(application, maxWaitSeconds);
342 if (ERROR_SUCCESS == ret) {
343 return 0;
344 } else if (WAIT_TIMEOUT == ret) {
345 return 1;
346 } else {
347 return 2;
348 }
349 #else
350 // Not implemented on non-Windows platforms
351 return 1;
352 #endif
353 }
354
355 if (!NS_tstrcmp(argv[1], NS_T("launch-service"))) {
356 #if defined(XP_WIN) && defined(MOZ_MAINTENANCE_SERVICE)
357 DWORD ret =
358 LaunchServiceSoftwareUpdateCommand(argc - 2, (LPCWSTR*)argv + 2);
359 if (ret != ERROR_SUCCESS) {
360 // 192 is used to avoid reusing a possible return value from the call to
361 // WaitForServiceStop
362 return 0x000000C0;
363 }
364 // Wait a maximum of 120 seconds.
365 DWORD lastState = WaitForServiceStop(SVC_NAME, 120);
366 if (SERVICE_STOPPED == lastState) {
367 return 0;
368 }
369 return lastState;
370 #else
371 // Not implemented on non-Windows platforms
372 return 1;
373 #endif
374 }
375
376 if (!NS_tstrcmp(argv[1], NS_T("create-update-dir"))) {
377 #ifdef XP_WIN
378 mozilla::UniquePtr<wchar_t[]> updateDir;
379 HRESULT result = GetCommonUpdateDirectory(argv[2], updateDir);
380 return SUCCEEDED(result) ? 0 : 1;
381 #else
382 // Not implemented on non-Windows platforms
383 return 1;
384 #endif
385 }
386
387 if (NS_tchdir(argv[1]) != 0) {
388 return 1;
389 }
390
391 // File in use test helper section
392 if (!NS_tstrcmp(argv[4], NS_T("-s"))) {
393 // Note: glibc's getcwd() allocates the buffer dynamically using malloc(3)
394 // if buf (the 1st param) is NULL so free cwd when it is no longer needed.
395 NS_tchar* cwd = NS_tgetcwd(nullptr, 0);
396 NS_tchar inFilePath[MAXPATHLEN];
397 if (!NS_tvsnprintf(inFilePath, sizeof(inFilePath) / sizeof(inFilePath[0]),
398 NS_T("%s/%s"), cwd, argv[2])) {
399 return 1;
400 }
401 NS_tchar outFilePath[MAXPATHLEN];
402 if (!NS_tvsnprintf(outFilePath,
403 sizeof(outFilePath) / sizeof(outFilePath[0]),
404 NS_T("%s/%s"), cwd, argv[3])) {
405 return 1;
406 }
407 free(cwd);
408
409 int seconds = NS_ttoi(argv[5]);
410 #ifdef XP_WIN
411 HANDLE hFile = INVALID_HANDLE_VALUE;
412 if (argc == 7) {
413 hFile = CreateFileW(argv[6], DELETE | GENERIC_WRITE, 0, nullptr,
414 OPEN_EXISTING, 0, nullptr);
415 if (hFile == INVALID_HANDLE_VALUE) {
416 WriteMsg(outFilePath, "error_locking");
417 return 1;
418 }
419 }
420
421 WriteMsg(outFilePath, "sleeping");
422 int i = 0;
423 while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds) {
424 Sleep(1000);
425 }
426
427 if (argc == 7) {
428 CloseHandle(hFile);
429 }
430 #else
431 WriteMsg(outFilePath, "sleeping");
432 int i = 0;
433 while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds) {
434 sleep(1);
435 }
436 #endif
437 WriteMsg(outFilePath, "finished");
438 return 0;
439 }
440
441 {
442 // Command line argument test helper section
443 NS_tchar logFilePath[MAXPATHLEN];
444 if (!NS_tvsnprintf(logFilePath,
445 sizeof(logFilePath) / sizeof(logFilePath[0]), NS_T("%s"),
446 argv[2])) {
447 return 1;
448 }
449
450 FILE* logFP = NS_tfopen(logFilePath, NS_T("wb"));
451 if (!logFP) {
452 return 1;
453 }
454 for (int i = 1; i < argc; ++i) {
455 fprintf(logFP, LOG_S "\n", argv[i]);
456 }
457
458 fclose(logFP);
459 logFP = nullptr;
460 }
461
462 return 0;
463 }
464