Exemple #1
0
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);
}
Exemple #2
0
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));
}
Exemple #3
0
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(&current_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(&parallels_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;
}
Exemple #5
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;
}
Exemple #6
0
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;
}