| 751 | |
| 752 | #ifdef _WIN32 |
| 753 | static bool bootstrap_production_spawn(void *context, |
| 754 | const cbm_daemon_bootstrap_launch_spec_t *spec) { |
| 755 | bootstrap_production_context_t *production = context; |
| 756 | if (production) { |
| 757 | production->spawn_error = ERROR_SUCCESS; |
| 758 | } |
| 759 | if (!spec || !spec->detached || spec->inherit_standard_handles || spec->use_shell) { |
| 760 | if (production) { |
| 761 | production->spawn_error = ERROR_INVALID_PARAMETER; |
| 762 | } |
| 763 | return false; |
| 764 | } |
| 765 | char command_line[BOOTSTRAP_PATH_CAP * 2]; |
| 766 | if (!cbm_build_win_cmdline(command_line, sizeof(command_line), spec->argv)) { |
| 767 | if (production) { |
| 768 | production->spawn_error = ERROR_INVALID_PARAMETER; |
| 769 | } |
| 770 | return false; |
| 771 | } |
| 772 | wchar_t *application = cbm_utf8_to_wide(spec->executable_path); |
| 773 | wchar_t *command = cbm_utf8_to_wide(command_line); |
| 774 | if (!application || !command) { |
| 775 | if (production) { |
| 776 | production->spawn_error = ERROR_NOT_ENOUGH_MEMORY; |
| 777 | } |
| 778 | free(application); |
| 779 | free(command); |
| 780 | return false; |
| 781 | } |
| 782 | STARTUPINFOW startup; |
| 783 | PROCESS_INFORMATION child; |
| 784 | ZeroMemory(&startup, sizeof(startup)); |
| 785 | ZeroMemory(&child, sizeof(child)); |
| 786 | startup.cb = sizeof(startup); |
| 787 | DWORD flags = DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW; |
| 788 | /* A managed frontend payload is intentionally contained in the permanent |
| 789 | * launcher's kill-on-close job. The account daemon outlives that one |
| 790 | * frontend and therefore uses breakaway when the containing job explicitly |
| 791 | * permits it. Do not request breakaway from an unrelated restrictive job: |
| 792 | * CreateProcess would fail and regress portable/package-manager payloads. */ |
| 793 | BOOL in_job = FALSE; |
| 794 | JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_limits; |
| 795 | memset(&job_limits, 0, sizeof(job_limits)); |
| 796 | if (IsProcessInJob(GetCurrentProcess(), NULL, &in_job) && in_job && |
| 797 | QueryInformationJobObject(NULL, JobObjectExtendedLimitInformation, &job_limits, |
| 798 | sizeof(job_limits), NULL) && |
| 799 | (job_limits.BasicLimitInformation.LimitFlags & |
| 800 | (JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)) != 0) { |
| 801 | flags |= CREATE_BREAKAWAY_FROM_JOB; |
| 802 | } |
| 803 | BOOL created = CreateProcessW(application, command, NULL, NULL, FALSE, flags, NULL, NULL, |
| 804 | &startup, &child); |
| 805 | DWORD spawn_error = created ? ERROR_SUCCESS : GetLastError(); |
| 806 | free(application); |
| 807 | free(command); |
| 808 | if (production) { |
| 809 | production->spawn_error = spawn_error; |
| 810 | } |
nothing calls this directly
no test coverage detected