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(
380         argv[2], SetPermissionsOf::BaseDirIfNotExists, updateDir);
381     return SUCCEEDED(result) ? 0 : 1;
382 #else
383     // Not implemented on non-Windows platforms
384     return 1;
385 #endif
386   }
387 
388   if (NS_tchdir(argv[1]) != 0) {
389     return 1;
390   }
391 
392   // File in use test helper section
393   if (!NS_tstrcmp(argv[4], NS_T("-s"))) {
394     // Note: glibc's getcwd() allocates the buffer dynamically using malloc(3)
395     // if buf (the 1st param) is NULL so free cwd when it is no longer needed.
396     NS_tchar* cwd = NS_tgetcwd(nullptr, 0);
397     NS_tchar inFilePath[MAXPATHLEN];
398     if (!NS_tvsnprintf(inFilePath, sizeof(inFilePath) / sizeof(inFilePath[0]),
399                        NS_T("%s/%s"), cwd, argv[2])) {
400       return 1;
401     }
402     NS_tchar outFilePath[MAXPATHLEN];
403     if (!NS_tvsnprintf(outFilePath,
404                        sizeof(outFilePath) / sizeof(outFilePath[0]),
405                        NS_T("%s/%s"), cwd, argv[3])) {
406       return 1;
407     }
408     free(cwd);
409 
410     int seconds = NS_ttoi(argv[5]);
411 #ifdef XP_WIN
412     HANDLE hFile = INVALID_HANDLE_VALUE;
413     if (argc == 7) {
414       hFile = CreateFileW(argv[6], DELETE | GENERIC_WRITE, 0, nullptr,
415                           OPEN_EXISTING, 0, nullptr);
416       if (hFile == INVALID_HANDLE_VALUE) {
417         WriteMsg(outFilePath, "error_locking");
418         return 1;
419       }
420     }
421 
422     WriteMsg(outFilePath, "sleeping");
423     int i = 0;
424     while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds) {
425       Sleep(1000);
426     }
427 
428     if (argc == 7) {
429       CloseHandle(hFile);
430     }
431 #else
432     WriteMsg(outFilePath, "sleeping");
433     int i = 0;
434     while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds) {
435       sleep(1);
436     }
437 #endif
438     WriteMsg(outFilePath, "finished");
439     return 0;
440   }
441 
442   {
443     // Command line argument test helper section
444     NS_tchar logFilePath[MAXPATHLEN];
445     if (!NS_tvsnprintf(logFilePath,
446                        sizeof(logFilePath) / sizeof(logFilePath[0]), NS_T("%s"),
447                        argv[2])) {
448       return 1;
449     }
450 
451     FILE* logFP = NS_tfopen(logFilePath, NS_T("wb"));
452     if (!logFP) {
453       return 1;
454     }
455     for (int i = 1; i < argc; ++i) {
456       fprintf(logFP, LOG_S "\n", argv[i]);
457     }
458 
459     fclose(logFP);
460     logFP = nullptr;
461   }
462 
463   return 0;
464 }
465