michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ michael@0: */ michael@0: michael@0: #ifdef XP_WIN michael@0: # include michael@0: # include michael@0: # include michael@0: # include michael@0: # include michael@0: # include michael@0: typedef WCHAR NS_tchar; michael@0: # define NS_main wmain michael@0: # define F_OK 00 michael@0: # define W_OK 02 michael@0: # define R_OK 04 michael@0: # define stat _stat michael@0: # define NS_T(str) L ## str michael@0: # define NS_tsnprintf(dest, count, fmt, ...) \ michael@0: { \ michael@0: int _count = count - 1; \ michael@0: _snwprintf(dest, _count, fmt, ##__VA_ARGS__); \ michael@0: dest[_count] = L'\0'; \ michael@0: } michael@0: # define NS_taccess _waccess michael@0: # define NS_tchdir _wchdir michael@0: # define NS_tfopen _wfopen michael@0: # define NS_tstrcmp wcscmp michael@0: # define NS_ttoi _wtoi michael@0: # define NS_tstat _wstat michael@0: # define NS_tgetcwd _wgetcwd michael@0: # define LOG_S "%S" michael@0: michael@0: #include "../common/updatehelper.h" michael@0: michael@0: #else michael@0: # include michael@0: # define NS_main main michael@0: typedef char NS_tchar; michael@0: # define NS_T(str) str michael@0: # define NS_tsnprintf snprintf michael@0: # define NS_taccess access michael@0: # define NS_tchdir chdir michael@0: # define NS_tfopen fopen michael@0: # define NS_tstrcmp strcmp michael@0: # define NS_ttoi atoi michael@0: # define NS_tstat stat michael@0: # define NS_tgetcwd getcwd michael@0: # define NS_tfputs fputs michael@0: # define LOG_S "%s" michael@0: #endif michael@0: michael@0: #include "mozilla/NullPtr.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #ifndef MAXPATHLEN michael@0: # ifdef PATH_MAX michael@0: # define MAXPATHLEN PATH_MAX michael@0: # elif defined(MAX_PATH) michael@0: # define MAXPATHLEN MAX_PATH michael@0: # elif defined(_MAX_PATH) michael@0: # define MAXPATHLEN _MAX_PATH michael@0: # elif defined(CCHMAXPATH) michael@0: # define MAXPATHLEN CCHMAXPATH michael@0: # else michael@0: # define MAXPATHLEN 1024 michael@0: # endif michael@0: #endif michael@0: michael@0: static void michael@0: WriteMsg(const NS_tchar *path, const char *status) michael@0: { michael@0: FILE* outFP = NS_tfopen(path, NS_T("wb")); michael@0: if (!outFP) michael@0: return; michael@0: michael@0: fprintf(outFP, "%s\n", status); michael@0: fclose(outFP); michael@0: outFP = nullptr; michael@0: } michael@0: michael@0: static bool michael@0: CheckMsg(const NS_tchar *path, const char *expected) michael@0: { michael@0: if (NS_taccess(path, F_OK)) { michael@0: return false; michael@0: } michael@0: michael@0: FILE *inFP = NS_tfopen(path, NS_T("rb")); michael@0: if (!inFP) { michael@0: return false; michael@0: } michael@0: michael@0: struct stat ms; michael@0: if (fstat(fileno(inFP), &ms)) { michael@0: return false; michael@0: } michael@0: michael@0: char *mbuf = (char *) malloc(ms.st_size + 1); michael@0: if (!mbuf) { michael@0: return false; michael@0: } michael@0: michael@0: size_t r = ms.st_size; michael@0: char *rb = mbuf; michael@0: size_t c = fread(rb, sizeof(char), 50, inFP); michael@0: r -= c; michael@0: rb += c; michael@0: if (c == 0 && r) { michael@0: return false; michael@0: } michael@0: mbuf[ms.st_size] = '\0'; michael@0: rb = mbuf; michael@0: michael@0: fclose(inFP); michael@0: inFP = nullptr; michael@0: return strcmp(rb, expected) == 0; michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: /** michael@0: * Verifies the trust of the specified file path. michael@0: * michael@0: * @param filePath The file path to check. michael@0: * @return ERROR_SUCCESS if successful, or the last error code otherwise. michael@0: */ michael@0: DWORD michael@0: VerifyCertificateTrustForFile(LPCWSTR filePath) michael@0: { michael@0: // Setup the file to check. michael@0: WINTRUST_FILE_INFO fileToCheck; michael@0: ZeroMemory(&fileToCheck, sizeof(fileToCheck)); michael@0: fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO); michael@0: fileToCheck.pcwszFilePath = filePath; michael@0: michael@0: // Setup what to check, we want to check it is signed and trusted. michael@0: WINTRUST_DATA trustData; michael@0: ZeroMemory(&trustData, sizeof(trustData)); michael@0: trustData.cbStruct = sizeof(trustData); michael@0: trustData.pPolicyCallbackData = nullptr; michael@0: trustData.pSIPClientData = nullptr; michael@0: trustData.dwUIChoice = WTD_UI_NONE; michael@0: trustData.fdwRevocationChecks = WTD_REVOKE_NONE; michael@0: trustData.dwUnionChoice = WTD_CHOICE_FILE; michael@0: trustData.dwStateAction = 0; michael@0: trustData.hWVTStateData = nullptr; michael@0: trustData.pwszURLReference = nullptr; michael@0: // no UI michael@0: trustData.dwUIContext = 0; michael@0: trustData.pFile = &fileToCheck; michael@0: michael@0: GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2; michael@0: // Check if the file is signed by something that is trusted. michael@0: return WinVerifyTrust(nullptr, &policyGUID, &trustData); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: int NS_main(int argc, NS_tchar **argv) michael@0: { michael@0: michael@0: if (argc < 3) { michael@0: fprintf(stderr, \ michael@0: "\n" \ michael@0: "Application Update Service Test Helper\n" \ michael@0: "\n" \ michael@0: "Usage: WORKINGDIR INFILE OUTFILE -s SECONDS [FILETOLOCK]\n" \ michael@0: " or: WORKINGDIR LOGFILE [ARG2 ARG3...]\n" \ michael@0: " or: signature-check filepath\n" \ michael@0: " or: setup-symlink dir1 dir2 file symlink\n" \ michael@0: " or: remove-symlink dir1 dir2 file symlink\n" \ michael@0: " or: check-symlink symlink\n" \ michael@0: "\n" \ michael@0: " WORKINGDIR \tThe relative path to the working directory to use.\n" \ michael@0: " INFILE \tThe relative path from the working directory for the file to\n" \ michael@0: " \tread actions to perform such as finish.\n" \ michael@0: " OUTFILE \tThe relative path from the working directory for the file to\n" \ michael@0: " \twrite status information.\n" \ michael@0: " SECONDS \tThe number of seconds to sleep.\n" \ michael@0: " FILETOLOCK \tThe relative path from the working directory to an existing\n" \ michael@0: " \tfile to open exlusively.\n" \ michael@0: " \tOnly available on Windows platforms and silently ignored on\n" \ michael@0: " \tother platforms.\n" \ michael@0: " LOGFILE \tThe relative path from the working directory to log the\n" \ michael@0: " \tcommand line arguments.\n" \ michael@0: " ARG2 ARG3...\tArguments to write to the LOGFILE after the preceding command\n" \ michael@0: " \tline arguments.\n" \ michael@0: "\n" \ michael@0: "Note: All paths must be relative.\n" \ michael@0: "\n"); michael@0: return 1; michael@0: } michael@0: michael@0: if (!NS_tstrcmp(argv[1], NS_T("check-signature"))) { michael@0: #ifdef XP_WIN michael@0: if (ERROR_SUCCESS == VerifyCertificateTrustForFile(argv[2])) { michael@0: return 0; michael@0: } else { michael@0: return 1; michael@0: } michael@0: #else michael@0: // Not implemented on non-Windows platforms michael@0: return 1; michael@0: #endif michael@0: } michael@0: michael@0: if (!NS_tstrcmp(argv[1], NS_T("setup-symlink"))) { michael@0: #ifdef XP_UNIX michael@0: NS_tchar path[MAXPATHLEN]; michael@0: NS_tsnprintf(path, sizeof(path)/sizeof(path[0]), michael@0: NS_T("%s/%s"), NS_T("/tmp"), argv[2]); michael@0: mkdir(path, 0755); michael@0: NS_tsnprintf(path, sizeof(path)/sizeof(path[0]), michael@0: NS_T("%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3]); michael@0: mkdir(path, 0755); michael@0: NS_tsnprintf(path, sizeof(path)/sizeof(path[0]), michael@0: NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3], argv[4]); michael@0: FILE * file = NS_tfopen(path, NS_T("w")); michael@0: if (file) { michael@0: NS_tfputs(NS_T("test"), file); michael@0: fclose(file); michael@0: } michael@0: symlink(path, argv[5]); michael@0: NS_tsnprintf(path, sizeof(path)/sizeof(path[0]), michael@0: NS_T("%s/%s"), NS_T("/tmp"), argv[2]); michael@0: if (argc > 6 && !NS_tstrcmp(argv[6], NS_T("change-perm"))) { michael@0: chmod(path, 0644); michael@0: } michael@0: return 0; michael@0: #else michael@0: // Not implemented on non-Unix platforms michael@0: return 1; michael@0: #endif michael@0: } michael@0: michael@0: if (!NS_tstrcmp(argv[1], NS_T("remove-symlink"))) { michael@0: #ifdef XP_UNIX michael@0: NS_tchar path[MAXPATHLEN]; michael@0: NS_tsnprintf(path, sizeof(path)/sizeof(path[0]), michael@0: NS_T("%s/%s"), NS_T("/tmp"), argv[2]); michael@0: chmod(path, 0755); michael@0: NS_tsnprintf(path, sizeof(path)/sizeof(path[0]), michael@0: NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3], argv[4]); michael@0: unlink(path); michael@0: NS_tsnprintf(path, sizeof(path)/sizeof(path[0]), michael@0: NS_T("%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3]); michael@0: rmdir(path); michael@0: NS_tsnprintf(path, sizeof(path)/sizeof(path[0]), michael@0: NS_T("%s/%s"), NS_T("/tmp"), argv[2]); michael@0: rmdir(path); michael@0: return 0; michael@0: #else michael@0: // Not implemented on non-Unix platforms michael@0: return 1; michael@0: #endif michael@0: } michael@0: michael@0: if (!NS_tstrcmp(argv[1], NS_T("check-symlink"))) { michael@0: #ifdef XP_UNIX michael@0: struct stat ss; michael@0: lstat(argv[2], &ss); michael@0: return S_ISLNK(ss.st_mode) ? 0 : 1; michael@0: #else michael@0: // Not implemented on non-Unix platforms michael@0: return 1; michael@0: #endif michael@0: } michael@0: michael@0: if (!NS_tstrcmp(argv[1], NS_T("wait-for-service-stop"))) { michael@0: #ifdef XP_WIN michael@0: const int maxWaitSeconds = NS_ttoi(argv[3]); michael@0: LPCWSTR serviceName = argv[2]; michael@0: DWORD serviceState = WaitForServiceStop(serviceName, maxWaitSeconds); michael@0: if (SERVICE_STOPPED == serviceState) { michael@0: return 0; michael@0: } else { michael@0: return serviceState; michael@0: } michael@0: #else michael@0: // Not implemented on non-Windows platforms michael@0: return 1; michael@0: #endif michael@0: } michael@0: michael@0: if (!NS_tstrcmp(argv[1], NS_T("wait-for-application-exit"))) { michael@0: #ifdef XP_WIN michael@0: const int maxWaitSeconds = NS_ttoi(argv[3]); michael@0: LPCWSTR application = argv[2]; michael@0: DWORD ret = WaitForProcessExit(application, maxWaitSeconds); michael@0: if (ERROR_SUCCESS == ret) { michael@0: return 0; michael@0: } else if (WAIT_TIMEOUT == ret) { michael@0: return 1; michael@0: } else { michael@0: return 2; michael@0: } michael@0: #else michael@0: // Not implemented on non-Windows platforms michael@0: return 1; michael@0: #endif michael@0: } michael@0: michael@0: int i = 0; michael@0: michael@0: if (NS_tchdir(argv[1]) != 0) { michael@0: return 1; michael@0: } michael@0: michael@0: // File in use test helper section michael@0: if (!NS_tstrcmp(argv[4], NS_T("-s"))) { michael@0: NS_tchar *cwd = NS_tgetcwd(nullptr, 0); michael@0: NS_tchar inFilePath[MAXPATHLEN]; michael@0: NS_tsnprintf(inFilePath, sizeof(inFilePath)/sizeof(inFilePath[0]), michael@0: NS_T("%s/%s"), cwd, argv[2]); michael@0: NS_tchar outFilePath[MAXPATHLEN]; michael@0: NS_tsnprintf(outFilePath, sizeof(outFilePath)/sizeof(outFilePath[0]), michael@0: NS_T("%s/%s"), cwd, argv[3]); michael@0: michael@0: int seconds = NS_ttoi(argv[5]); michael@0: #ifdef XP_WIN michael@0: HANDLE hFile = INVALID_HANDLE_VALUE; michael@0: if (argc == 7) { michael@0: hFile = CreateFileW(argv[6], michael@0: DELETE | GENERIC_WRITE, 0, michael@0: nullptr, OPEN_EXISTING, 0, nullptr); michael@0: if (hFile == INVALID_HANDLE_VALUE) { michael@0: WriteMsg(outFilePath, "error_locking"); michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: WriteMsg(outFilePath, "sleeping"); michael@0: while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds) { michael@0: Sleep(1000); michael@0: } michael@0: michael@0: if (argc == 7) { michael@0: CloseHandle(hFile); michael@0: } michael@0: #else michael@0: WriteMsg(outFilePath, "sleeping"); michael@0: while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds) { michael@0: sleep(1); michael@0: } michael@0: #endif michael@0: WriteMsg(outFilePath, "finished"); michael@0: return 0; michael@0: } michael@0: michael@0: // Command line argument test helper section michael@0: NS_tchar logFilePath[MAXPATHLEN]; michael@0: NS_tsnprintf(logFilePath, sizeof(logFilePath)/sizeof(logFilePath[0]), michael@0: NS_T("%s"), argv[2]); michael@0: michael@0: FILE* logFP = NS_tfopen(logFilePath, NS_T("wb")); michael@0: for (i = 1; i < argc; ++i) { michael@0: fprintf(logFP, LOG_S "\n", argv[i]); michael@0: } michael@0: michael@0: fclose(logFP); michael@0: logFP = nullptr; michael@0: michael@0: return 0; michael@0: }