ATF_TC_BODY(fork_exec_wait__ok, tc) { const char* const args[] = {"sh", "-c", "exit 42", NULL}; int exitstatus = -1; // Shut up GCC warning. const kyua_error_t error = exec_check("/bin/sh", args, &exitstatus); ATF_REQUIRE(!kyua_error_is_set(error)); ATF_REQUIRE_EQ(42, exitstatus); }
ATF_TC_BODY(fork_exec_wait__enoent, tc) { const char* const args[] = {"foo", NULL}; int unused_exitstatus; const kyua_error_t error = exec_check("./foo", args, &unused_exitstatus); ATF_REQUIRE(kyua_error_is_set(error)); ATF_REQUIRE(kyua_error_is_type(error, "libc")); ATF_REQUIRE_EQ(ENOENT, kyua_libc_error_errno(error)); }
ATF_TC_BODY(fork_exec_wait__eacces, tc) { ATF_REQUIRE(mkdir("dir", 0000) != -1); const char* const args[] = {"foo", NULL}; int unused_exitstatus; const kyua_error_t error = exec_check("./dir/foo", args, &unused_exitstatus); ATF_REQUIRE(kyua_error_is_set(error)); ATF_REQUIRE(kyua_error_is_type(error, "libc")); ATF_REQUIRE_EQ(EACCES, kyua_libc_error_errno(error)); }
int main(void) { /* enable functions */ BOOL ENABLE_TLS_CHECKS = TRUE; BOOL ENABLE_DEBUG_CHECKS = TRUE; BOOL ENABLE_INJECTION_CHECKS = TRUE; BOOL ENABLE_GEN_SANDBOX_CHECKS = TRUE; BOOL ENABLE_VBOX_CHECKS = TRUE; BOOL ENABLE_VMWARE_CHECKS = TRUE; BOOL ENABLE_VPC_CHECKS = TRUE; BOOL ENABLE_QEMU_CHECKS = TRUE; BOOL ENABLE_XEN_CHECKS = TRUE; BOOL ENABLE_WINE_CHECKS = TRUE; BOOL ENABLE_PARALLELS_CHECKS = TRUE; BOOL ENABLE_CODE_INJECTIONS = FALSE; BOOL ENABLE_TIMING_ATTACKS = TRUE; BOOL ENABLE_DUMPING_CHECK = TRUE; BOOL ENABLE_ANALYSIS_TOOLS_CHECK = TRUE; /* Resize the console window for better visibility */ resize_console_window(); /* Display general informations */ _tprintf(_T("[al-khaser version 0.77]")); if (IsWoW64()) _tprintf(_T("Process is running under WOW64\n\n")); print_category(TEXT("Initialisation")); API::Init(); print_os(); API::PrintAvailabilityReport(); if (ENABLE_DEBUG_CHECKS) PageExceptionInitialEnum(); /* TLS checks */ if (ENABLE_TLS_CHECKS) { print_category(TEXT("TLS Callbacks")); exec_check(&TLSCallbackProcess, TEXT("TLS process attach callback ")); exec_check(&TLSCallbackThread, TEXT("TLS thread attach callback ")); } /* Debugger Detection */ if (ENABLE_DEBUG_CHECKS) { print_category(TEXT("Debugger Detection")); exec_check(&IsDebuggerPresentAPI, TEXT("Checking IsDebuggerPresent API ")); exec_check(&IsDebuggerPresentPEB, TEXT("Checking PEB.BeingDebugged ")); exec_check(&CheckRemoteDebuggerPresentAPI, TEXT("Checking CheckRemoteDebuggerPresent API ")); exec_check(&NtGlobalFlag, TEXT("Checking PEB.NtGlobalFlag ")); exec_check(&HeapFlags, TEXT("Checking ProcessHeap.Flags ")); exec_check(&HeapForceFlags, TEXT("Checking ProcessHeap.ForceFlags ")); exec_check(&NtQueryInformationProcess_ProcessDebugPort, TEXT("Checking NtQueryInformationProcess with ProcessDebugPort ")); exec_check(&NtQueryInformationProcess_ProcessDebugFlags, TEXT("Checking NtQueryInformationProcess with ProcessDebugFlags ")); exec_check(&NtQueryInformationProcess_ProcessDebugObject, TEXT("Checking NtQueryInformationProcess with ProcessDebugObject ")); exec_check(&WUDF_IsAnyDebuggerPresent, TEXT("Checking WudfIsAnyDebuggerPresent API ")); exec_check(&WUDF_IsKernelDebuggerPresent, TEXT("Checking WudfIsKernelDebuggerPresent API ")); exec_check(&WUDF_IsUserDebuggerPresent, TEXT("Checking WudfIsUserDebuggerPresent API ")); exec_check(&NtSetInformationThread_ThreadHideFromDebugger, TEXT("Checking NtSetInformationThread with ThreadHideFromDebugger ")); exec_check(&CloseHandle_InvalideHandle, TEXT("Checking CloseHandle with an invalide handle ")); exec_check(&UnhandledExcepFilterTest, TEXT("Checking UnhandledExcepFilterTest ")); exec_check(&OutputDebugStringAPI, TEXT("Checking OutputDebugString ")); exec_check(&HardwareBreakpoints, TEXT("Checking Hardware Breakpoints ")); exec_check(&SoftwareBreakpoints, TEXT("Checking Software Breakpoints ")); exec_check(&Interrupt_0x2d, TEXT("Checking Interupt 0x2d ")); exec_check(&Interrupt_3, TEXT("Checking Interupt 1 ")); exec_check(&MemoryBreakpoints_PageGuard, TEXT("Checking Memory Breakpoints PAGE GUARD ")); exec_check(&IsParentExplorerExe, TEXT("Checking If Parent Process is explorer.exe ")); exec_check(&CanOpenCsrss, TEXT("Checking SeDebugPrivilege ")); exec_check(&NtQueryObject_ObjectTypeInformation, TEXT("Checking NtQueryObject with ObjectTypeInformation ")); exec_check(&NtQueryObject_ObjectAllTypesInformation, TEXT("Checking NtQueryObject with ObjectAllTypesInformation ")); exec_check(&NtYieldExecutionAPI, TEXT("Checking NtYieldExecution ")); exec_check(&SetHandleInformatiom_ProtectedHandle, TEXT("Checking CloseHandle protected handle trick ")); exec_check(&NtQuerySystemInformation_SystemKernelDebuggerInformation, TEXT("Checking NtQuerySystemInformation with SystemKernelDebuggerInformation ")); exec_check(&SharedUserData_KernelDebugger, TEXT("Checking SharedUserData->KdDebuggerEnabled ")); exec_check(&ProcessJob, TEXT("Checking if process is in a job ")); exec_check(&VirtualAlloc_WriteWatch_BufferOnly, TEXT("Checking VirtualAlloc write watch (buffer only) ")); exec_check(&VirtualAlloc_WriteWatch_APICalls, TEXT("Checking VirtualAlloc write watch (API calls) ")); exec_check(&VirtualAlloc_WriteWatch_IsDebuggerPresent, TEXT("Checking VirtualAlloc write watch (IsDebuggerPresent) ")); exec_check(&VirtualAlloc_WriteWatch_CodeWrite, TEXT("Checking VirtualAlloc write watch (code write) ")); exec_check(&PageExceptionBreakpointCheck, TEXT("Checking for page exception breakpoints ")); exec_check(&ModuleBoundsHookCheck, TEXT("Checking for API hooks outside module bounds ")); } if (ENABLE_INJECTION_CHECKS) { print_category(TEXT("DLL Injection Detection")); exec_check(&ScanForModules_EnumProcessModulesEx_32bit, TEXT("Enumerating modules with EnumProcessModulesEx [32-bit] ")); exec_check(&ScanForModules_EnumProcessModulesEx_64bit, TEXT("Enumerating modules with EnumProcessModulesEx [64-bit] ")); exec_check(&ScanForModules_EnumProcessModulesEx_All, TEXT("Enumerating modules with EnumProcessModulesEx [ALL] ")); exec_check(&ScanForModules_ToolHelp32, TEXT("Enumerating modules with ToolHelp32 ")); exec_check(&ScanForModules_LdrEnumerateLoadedModules, TEXT("Enumerating the process LDR via LdrEnumerateLoadedModules ")); exec_check(&ScanForModules_LDR_Direct, TEXT("Enumerating the process LDR directly ")); exec_check(&ScanForModules_MemoryWalk_GMI, TEXT("Walking process memory with GetModuleInformation ")); exec_check(&ScanForModules_MemoryWalk_Hidden, TEXT("Walking process memory for hidden modules ")); } /* Generic sandbox detection */ if (ENABLE_GEN_SANDBOX_CHECKS) { print_category(TEXT("Generic Sandboxe/VM Detection")); loaded_dlls(); exec_check(&NumberOfProcessors, TEXT("Checking Number of processors in machine ")); exec_check(&idt_trick, TEXT("Checking Interupt Descriptor Table location ")); exec_check(&ldt_trick, TEXT("Checking Local Descriptor Table location ")); exec_check(&gdt_trick, TEXT("Checking Global Descriptor Table location ")); exec_check(&str_trick, TEXT("Checking Store Task Register ")); exec_check(&number_cores_wmi, TEXT("Checking Number of cores in machine using WMI ")); exec_check(&disk_size_wmi, TEXT("Checking hard disk size using WMI ")); exec_check(&dizk_size_deviceiocontrol, TEXT("Checking hard disk size using DeviceIoControl ")); exec_check(&setupdi_diskdrive, TEXT("Checking SetupDi_diskdrive ")); exec_check(&mouse_movement, TEXT("Checking mouse movement ")); exec_check(&memory_space, TEXT("Checking memory space using GlobalMemoryStatusEx ")); exec_check(&disk_size_getdiskfreespace, TEXT("Checking disk size using GetDiskFreeSpaceEx ")); exec_check(&cpuid_is_hypervisor, TEXT("Checking if CPU hypervisor field is set using cpuid(0x1)")); exec_check(&cpuid_hypervisor_vendor, TEXT("Checking hypervisor vendor using cpuid(0x40000000)")); exec_check(&accelerated_sleep, TEXT("Check if time has been accelerated ")); exec_check(&VMDriverServices, TEXT("VM Driver Services ")); exec_check(&serial_number_bios_wmi, TEXT("Checking SerialNumber from BIOS using WMI ")); exec_check(&model_computer_system_wmi, TEXT("Checking Model from ComputerSystem using WMI ")); exec_check(&manufacturer_computer_system_wmi, TEXT("Checking Manufacturer from ComputerSystem using WMI ")); exec_check(¤t_temperature_acpi_wmi, TEXT("Checking Current Temperature using WMI ")); exec_check(&process_id_processor_wmi, TEXT("Checking ProcessId using WMI ")); exec_check(&power_capabilities, TEXT("Checking power capabilities ")); exec_check(&cpu_fan_wmi, TEXT("Checking CPU fan using WMI ")); exec_check(&query_license_value, TEXT("Checking NtQueryLicenseValue with Kernel-VMDetection-Private ")); exec_check(&cachememory_wmi, TEXT("Checking Win32_CacheMemory with WMI ")); exec_check(&physicalmemory_wmi, TEXT("Checking Win32_PhysicalMemory with WMI ")); exec_check(&memorydevice_wmi, TEXT("Checking Win32_MemoryDevice with WMI ")); exec_check(&memoryarray_wmi, TEXT("Checking Win32_MemoryArray with WMI ")); exec_check(&voltageprobe_wmi, TEXT("Checking Win32_VoltageProbe with WMI ")); exec_check(&portconnector_wmi, TEXT("Checking Win32_PortConnector with WMI ")); exec_check(&smbiosmemory_wmi, TEXT("Checking Win32_SMBIOSMemory with WMI ")); exec_check(&perfctrs_thermalzoneinfo_wmi, TEXT("Checking ThermalZoneInfo performance counters with WMI ")); exec_check(&cim_memory_wmi, TEXT("Checking CIM_Memory with WMI ")); exec_check(&cim_sensor_wmi, TEXT("Checking CIM_Sensor with WMI ")); exec_check(&cim_numericsensor_wmi, TEXT("Checking CIM_NumericSensor with WMI ")); exec_check(&cim_temperaturesensor_wmi, TEXT("Checking CIM_TemperatureSensor with WMI ")); exec_check(&cim_voltagesensor_wmi, TEXT("Checking CIM_VoltageSensor with WMI ")); exec_check(&cim_physicalconnector_wmi, TEXT("Checking CIM_PhysicalConnector with WMI ")); exec_check(&cim_slot_wmi, TEXT("Checking CIM_Slot with WMI ")); } /* VirtualBox Detection */ if (ENABLE_VBOX_CHECKS) { print_category(TEXT("VirtualBox Detection")); vbox_reg_key_value(); exec_check(&vbox_dir, TEXT("Checking VirtualBox Guest Additions directory ")); vbox_files(); vbox_reg_keys(); exec_check(&vbox_check_mac, TEXT("Checking Mac Address start with 08:00:27 ")); exec_check(&hybridanalysismacdetect, TEXT("Checking MAC address (Hybrid Analysis) ")); vbox_devices(); exec_check(&vbox_window_class, TEXT("Checking VBoxTrayToolWndClass / VBoxTrayToolWnd ")); exec_check(&vbox_network_share, TEXT("Checking VirtualBox Shared Folders network provider ")); vbox_processes(); exec_check(&vbox_pnpentity_pcideviceid_wmi, TEXT("Checking Win32_PnPDevice DeviceId from WMI for VBox PCI device ")); exec_check(&vbox_pnpentity_controllers_wmi, TEXT("Checking Win32_PnPDevice Name from WMI for VBox controller hardware ")); exec_check(&vbox_pnpentity_vboxname_wmi, TEXT("Checking Win32_PnPDevice Name from WMI for VBOX names ")); exec_check(&vbox_bus_wmi, TEXT("Checking Win32_Bus from WMI ")); exec_check(&vbox_baseboard_wmi, TEXT("Checking Win32_BaseBoard from WMI ")); exec_check(&vbox_mac_wmi, TEXT("Checking MAC address from WMI ")); exec_check(&vbox_eventlogfile_wmi, TEXT("Checking NTEventLog from WMI ")); exec_check(&vbox_firmware_SMBIOS, TEXT("Checking SMBIOS firmware ")); exec_check(&vbox_firmware_ACPI, TEXT("Checking ACPI tables ")); } /* VMWare Detection */ if (ENABLE_VMWARE_CHECKS) { print_category(TEXT("VMWare Detection")); vmware_reg_key_value(); vmware_reg_keys(); vmware_files(); vmware_mac(); exec_check(&vmware_adapter_name, TEXT("Checking VMWare network adapter name ")); vmware_devices(); exec_check(&vmware_dir, TEXT("Checking VMWare directory ")); exec_check(&vmware_firmware_SMBIOS, TEXT("Checking SMBIOS firmware ")); exec_check(&vmware_firmware_ACPI, TEXT("Checking ACPI tables ")); } /* Virtual PC Detection */ if (ENABLE_VPC_CHECKS) { print_category(TEXT("Virtual PC Detection")); virtual_pc_process(); virtual_pc_reg_keys(); } /* QEMU Detection */ if (ENABLE_QEMU_CHECKS) { print_category(TEXT("QEMU Detection")); qemu_reg_key_value(); qemu_processes(); exec_check(&qemu_firmware_SMBIOS, TEXT("Checking SMBIOS firmware ")); exec_check(&qemu_firmware_ACPI, TEXT("Checking ACPI tables ")); } /* Xen Detection */ if (ENABLE_XEN_CHECKS) { print_category(TEXT("Xen Detection")); xen_process(); exec_check(&xen_check_mac, TEXT("Checking Mac Address start with 08:16:3E ")); } /* Wine Detection */ if (ENABLE_WINE_CHECKS) { print_category(TEXT("Wine Detection")); exec_check(&wine_exports, TEXT("Checking Wine via dll exports ")); wine_reg_keys(); } /* Paralles Detection */ if (ENABLE_PARALLELS_CHECKS) { print_category(TEXT("Paralles Detection")); parallels_process(); exec_check(¶llels_check_mac, TEXT("Checking Mac Address start with 08:1C:42 ")); } /* Code injections techniques */ if (ENABLE_CODE_INJECTIONS) { CreateRemoteThread_Injection(); SetWindowsHooksEx_Injection(); NtCreateThreadEx_Injection(); RtlCreateUserThread_Injection(); QueueUserAPC_Injection(); GetSetThreadContext_Injection(); } /* Timing Attacks */ if (ENABLE_TIMING_ATTACKS) { print_category(TEXT("Timing-attacks")); UINT delayInSeconds = 600U; UINT delayInMillis = delayInSeconds * 1000U; printf("\n[*] Delay value is set to %u minutes ...\n", delayInSeconds / 60); exec_check(timing_NtDelayexecution, delayInMillis, TEXT("Performing a sleep using NtDelayExecution ...")); exec_check(timing_sleep_loop, delayInMillis, TEXT("Performing a sleep() in a loop ...")); exec_check(timing_SetTimer, delayInMillis, TEXT("Delaying execution using SetTimer ...")); exec_check(timing_timeSetEvent, delayInMillis, TEXT("Delaying execution using timeSetEvent ...")); exec_check(timing_WaitForSingleObject, delayInMillis, TEXT("Delaying execution using WaitForSingleObject ...")); exec_check(timing_IcmpSendEcho, delayInMillis, TEXT("Delaying execution using IcmpSendEcho ...")); exec_check(timing_CreateWaitableTimer, delayInMillis, TEXT("Delaying execution using CreateWaitableTimer ...")); exec_check(timing_CreateTimerQueueTimer, delayInMillis, TEXT("Delaying execution using CreateTimerQueueTimer ...")); exec_check(&rdtsc_diff_locky, TEXT("Checking RDTSC Locky trick ")); exec_check(&rdtsc_diff_vmexit, TEXT("Checking RDTSC which force a VM Exit (cpuid) ")); } /* Malware analysis tools */ if (ENABLE_ANALYSIS_TOOLS_CHECK) { print_category(TEXT("Analysis-tools")); analysis_tools_process(); } /* Anti Dumping */ if (ENABLE_DUMPING_CHECK) { print_category(TEXT("Anti Dumping")); ErasePEHeaderFromMemory(); SizeOfImage(); } _tprintf(_T("\n\nAnalysis done, I hope you didn't get red flags :)")); getchar(); return 0; }
static CMD * make1cmds( TARGET * t ) { CMD * cmds = 0; CMD * * cmds_next = &cmds; LIST * shell = L0; module_t * settings_module = 0; TARGET * settings_target = 0; ACTIONS * a0; int const running_flag = globs.noexec ? A_RUNNING_NOEXEC : A_RUNNING; /* Step through actions. Actions may be shared with other targets or grouped * using RULE_TOGETHER, so actions already seen are skipped. */ for ( a0 = t->actions; a0; a0 = a0->next ) { RULE * rule = a0->action->rule; rule_actions * actions = rule->actions; SETTINGS * boundvars; LIST * nt; LIST * ns; ACTIONS * a1; /* Only do rules with commands to execute. If this action has already * been executed, use saved status. */ if ( !actions || a0->action->running >= running_flag ) continue; a0->action->running = running_flag; /* Make LISTS of targets and sources. If `execute together` has been * specified for this rule, tack on sources from each instance of this * rule for this target. */ nt = make1list( L0, a0->action->targets, 0 ); ns = make1list( L0, a0->action->sources, actions->flags ); if ( actions->flags & RULE_TOGETHER ) for ( a1 = a0->next; a1; a1 = a1->next ) if ( a1->action->rule == rule && a1->action->running < running_flag ) { ns = make1list( ns, a1->action->sources, actions->flags ); a1->action->running = running_flag; } /* If doing only updated (or existing) sources, but none have been * updated (or exist), skip this action. */ if ( list_empty( ns ) && ( actions->flags & ( RULE_NEWSRCS | RULE_EXISTING ) ) ) { list_free( nt ); continue; } swap_settings( &settings_module, &settings_target, rule->module, t ); if ( list_empty( shell ) ) { /* shell is per-target */ shell = var_get( rule->module, constant_JAMSHELL ); } /* If we had 'actions xxx bind vars' we bind the vars now. */ boundvars = make1settings( rule->module, actions->bindlist ); pushsettings( rule->module, boundvars ); /* * Build command, starting with all source args. * * For actions that allow PIECEMEAL commands, if the constructed command * string is too long, we retry constructing it with a reduced number of * source arguments presented. * * While reducing slowly takes a bit of compute time to get things just * right, it is worth it to get as close to maximum allowed command * string length as possible, because launching the commands we are * executing is likely to be much more compute intensive. * * Note that we loop through at least once, for sourceless actions. */ { int const length = list_length( ns ); int start = 0; int chunk = length; LIST * cmd_targets = L0; LIST * cmd_shell = L0; do { CMD * cmd; int cmd_check_result; int cmd_error_length; int cmd_error_max_length; int retry = 0; int accept_command = 0; /* Build cmd: cmd_new() takes ownership of its lists. */ if ( list_empty( cmd_targets ) ) cmd_targets = list_copy( nt ); if ( list_empty( cmd_shell ) ) cmd_shell = list_copy( shell ); cmd = cmd_new( rule, cmd_targets, list_sublist( ns, start, chunk ), cmd_shell ); cmd_check_result = exec_check( cmd->buf, &cmd->shell, &cmd_error_length, &cmd_error_max_length ); if ( cmd_check_result == EXEC_CHECK_OK ) { accept_command = 1; } else if ( cmd_check_result == EXEC_CHECK_NOOP ) { accept_command = 1; cmd->noop = 1; } else if ( ( actions->flags & RULE_PIECEMEAL ) && ( chunk > 1 ) ) { /* Too long but splittable. Reduce chunk size slowly and * retry. */ assert( cmd_check_result == EXEC_CHECK_TOO_LONG || cmd_check_result == EXEC_CHECK_LINE_TOO_LONG ); chunk = chunk * 9 / 10; retry = 1; } else { /* Too long and not splittable. */ char const * const error_message = cmd_check_result == EXEC_CHECK_TOO_LONG ? "is too long" : "contains a line that is too long"; assert( cmd_check_result == EXEC_CHECK_TOO_LONG || cmd_check_result == EXEC_CHECK_LINE_TOO_LONG ); printf( "%s action %s (%d, max %d):\n", object_str( rule->name ), error_message, cmd_error_length, cmd_error_max_length ); /* Tell the user what did not fit. */ fputs( cmd->buf->value, stdout ); exit( EXITBAD ); } assert( !retry || !accept_command ); if ( accept_command ) { /* Chain it up. */ *cmds_next = cmd; cmds_next = &cmd->next; /* Mark lists we need recreated for the next command since * they got consumed by the cmd object. */ cmd_targets = L0; cmd_shell = L0; } else { /* We can reuse targets & shell lists for the next command * if we do not let them die with this cmd object. */ cmd_release_targets_and_shell( cmd ); cmd_free( cmd ); } if ( !retry ) start += chunk; } while ( start < length ); } /* These were always copied when used. */ list_free( nt ); list_free( ns ); /* Free variables with values bound by 'actions xxx bind vars'. */ popsettings( rule->module, boundvars ); freesettings( boundvars ); } swap_settings( &settings_module, &settings_target, 0, 0 ); return cmds; }
static CMD * make1cmds( TARGET * t ) { CMD * cmds = 0; CMD * last_cmd; LIST * shell = L0; module_t * settings_module = 0; TARGET * settings_target = 0; ACTIONS * a0; int const running_flag = globs.noexec ? A_RUNNING_NOEXEC : A_RUNNING; /* Step through actions. */ for ( a0 = t->actions; a0; a0 = a0->next ) { RULE * rule = a0->action->rule; rule_actions * actions = rule->actions; SETTINGS * boundvars; LIST * nt; LIST * ns; ACTIONS * a1; /* Only do rules with commands to execute. */ if ( !actions ) continue; if ( a0->action->running >= running_flag ) { CMD * first; /* If this action was skipped either because it was * combined with another action by RULE_TOGETHER, or * because all of its sources were filtered out, * then we don't have anything to do here. */ if ( a0->action->first_cmd == NULL ) continue; /* This action has already been processed for another target. * Just set up the dependency graph correctly and move on. */ first = a0->action->first_cmd; if( cmds ) { last_cmd->next = cmdlist_append_cmd( last_cmd->next, first ); } else { cmds = first; } last_cmd = a0->action->last_cmd; continue; } a0->action->running = running_flag; /* Make LISTS of targets and sources. If `execute together` has been * specified for this rule, tack on sources from each instance of this * rule for this target. */ nt = make1list( L0, a0->action->targets, 0 ); ns = make1list( L0, a0->action->sources, actions->flags ); if ( actions->flags & RULE_TOGETHER ) for ( a1 = a0->next; a1; a1 = a1->next ) if ( a1->action->rule == rule && a1->action->running < running_flag && targets_equal( a0->action->targets, a1->action->targets ) ) { ns = make1list( ns, a1->action->sources, actions->flags ); a1->action->running = running_flag; } /* If doing only updated (or existing) sources, but none have been * updated (or exist), skip this action. */ if ( list_empty( ns ) && ( actions->flags & ( RULE_NEWSRCS | RULE_EXISTING ) ) ) { list_free( nt ); continue; } swap_settings( &settings_module, &settings_target, rule->module, t ); if ( list_empty( shell ) ) { /* shell is per-target */ shell = var_get( rule->module, constant_JAMSHELL ); } /* If we had 'actions xxx bind vars' we bind the vars now. */ boundvars = make1settings( rule->module, actions->bindlist ); pushsettings( rule->module, boundvars ); /* * Build command, starting with all source args. * * For actions that allow PIECEMEAL commands, if the constructed command * string is too long, we retry constructing it with a reduced number of * source arguments presented. * * While reducing slowly takes a bit of compute time to get things just * right, it is worth it to get as close to maximum allowed command * string length as possible, because launching the commands we are * executing is likely to be much more compute intensive. * * Note that we loop through at least once, for sourceless actions. */ { int const length = list_length( ns ); int start = 0; int chunk = length; int cmd_count = 0; LIST * cmd_targets = L0; LIST * cmd_shell = L0; TARGETS * semaphores = NULL; TARGETS * targets_iter; int unique_targets; do { CMD * cmd; int cmd_check_result; int cmd_error_length; int cmd_error_max_length; int retry = 0; int accept_command = 0; /* Build cmd: cmd_new() takes ownership of its lists. */ if ( list_empty( cmd_targets ) ) cmd_targets = list_copy( nt ); if ( list_empty( cmd_shell ) ) cmd_shell = list_copy( shell ); cmd = cmd_new( rule, cmd_targets, list_sublist( ns, start, chunk ), cmd_shell ); cmd_check_result = exec_check( cmd->buf, &cmd->shell, &cmd_error_length, &cmd_error_max_length ); if ( cmd_check_result == EXEC_CHECK_OK ) { accept_command = 1; } else if ( cmd_check_result == EXEC_CHECK_NOOP ) { accept_command = 1; cmd->noop = 1; } else if ( ( actions->flags & RULE_PIECEMEAL ) && ( chunk > 1 ) ) { /* Too long but splittable. Reduce chunk size slowly and * retry. */ assert( cmd_check_result == EXEC_CHECK_TOO_LONG || cmd_check_result == EXEC_CHECK_LINE_TOO_LONG ); chunk = chunk * 9 / 10; retry = 1; } else { /* Too long and not splittable. */ char const * const error_message = cmd_check_result == EXEC_CHECK_TOO_LONG ? "is too long" : "contains a line that is too long"; assert( cmd_check_result == EXEC_CHECK_TOO_LONG || cmd_check_result == EXEC_CHECK_LINE_TOO_LONG ); printf( "%s action %s (%d, max %d):\n", object_str( rule->name ), error_message, cmd_error_length, cmd_error_max_length ); /* Tell the user what did not fit. */ fputs( cmd->buf->value, stdout ); exit( EXITBAD ); } assert( !retry || !accept_command ); if ( accept_command ) { /* Chain it up. */ if ( cmds ) { last_cmd->next = cmdlist_append_cmd( last_cmd->next, cmd ); last_cmd = cmd; } else { cmds = last_cmd = cmd; } if ( cmd_count++ == 0 ) { a0->action->first_cmd = cmd; } /* Mark lists we need recreated for the next command since * they got consumed by the cmd object. */ cmd_targets = L0; cmd_shell = L0; } else { /* We can reuse targets & shell lists for the next command * if we do not let them die with this cmd object. */ cmd_release_targets_and_shell( cmd ); cmd_free( cmd ); } if ( !retry ) start += chunk; } while ( start < length ); /* Record the end of the actions cmds */ a0->action->last_cmd = last_cmd; unique_targets = 0; for ( targets_iter = a0->action->targets; targets_iter; targets_iter = targets_iter->next ) { if ( targets_contains( targets_iter->next, targets_iter->target ) ) continue; /* Add all targets produced by the action to the update list. */ push_state( &state_stack, targets_iter->target, NULL, T_STATE_MAKE1A ); ++unique_targets; } /* We need to wait until all the targets agree that * it's okay to run this action. */ ( ( CMD * )a0->action->first_cmd )->asynccnt = unique_targets; #if OPT_SEMAPHORE /* Collect semaphores */ for ( targets_iter = a0->action->targets; targets_iter; targets_iter = targets_iter->next ) { TARGET * sem = targets_iter->target->semaphore; if ( sem ) { TARGETS * semiter; if ( ! targets_contains( semaphores, sem ) ) semaphores = targetentry( semaphores, sem ); } } ( ( CMD * )a0->action->first_cmd )->lock = semaphores; ( ( CMD * )a0->action->last_cmd )->unlock = semaphores; #endif } /* These were always copied when used. */ list_free( nt ); list_free( ns ); /* Free variables with values bound by 'actions xxx bind vars'. */ popsettings( rule->module, boundvars ); freesettings( boundvars ); } if ( cmds ) { last_cmd->next = cmdlist_append_target( last_cmd->next, t ); } swap_settings( &settings_module, &settings_target, 0, 0 ); return cmds; }