UEZ Posted October 10, 2008 Share Posted October 10, 2008 (edited) A demonstration how to use pseudo multi processing to speed up collection of information of remote systems:Normally a script works sequentially, e.g. it checks one system by one. If a systems hangs then whole script hangs, too.How about to speed up script by using multiple processes?Let's see my solution for that!This command line tool S(ystem)I(nformation)C(ollector)2 aka SIC2 is helpful for system admins which need to collect data from their systems in an easy and fast way.SIC2 is a tool which can do that, particularly with ability of pseudo parallel multiple processes (multi threading).It connects e.g. via WMI to local and/or remote systems to collect data (see options below).Here the command line (use it in a CMD box):Usage: SIC2.exe [-i "<system filename="" list="">"][-master -slaves <n>] for multiprocessing mode[-ver] for version number[-prio <0-3>] for process prio - standard is 0 (idle)[<options>][-? | /? | -help | /help]] for this pageOptions can be:-os get only information about operating system-rl get only information about role of system-sv get only information about running servicesIf argument [-master -slaves <n>] is not given then sequentialmode will be used automatically!Output will be written to folder Output where SIC2.exe was started.Output format is a semicolon separated text file (*.csv).On each run output files will be overwritten!<system filename="" list=""> is a text file with the names of the systems in each line.An example of the command line could be: SIC2.exe -i serverlist.txt -master -slaves 15 -os -rl -sv -prio 1This will run SIC2.exe which will create 15 sub processes (slaves) which are connecting to the systems given in serverlist.txt.Of course it makes no sense to select 15 slaves when the list contains less systems!Currently only 3 modules are implemented. Feel free to upgrade it with more options. See below for a version with more modules!Benchmark on a terminal server with only 4 users, with gigabit network and SIC2.exe in idle priority:152 systems, only LAN connection, no WAN.WAN benchmark will follow hopefully.I hope this is tool is helpful for you! But have in mind that this project is still in beta phase and thus maybe buggy!Some parts also adjusted to my needs and will give no results probably on your systems.Here the code (early stage with 3 modules only):expandcollapse popup#AutoIt3Wrapper_UseX64=n #AutoIt3Wrapper_Change2CUI=y #AutoIt3Wrapper_Res_Description=System Information Collector #AutoIt3Wrapper_Res_Fileversion=2.0.0.0 #AutoIt3Wrapper_Res_Language=1033 #AutoIt3Wrapper_res_requestedExecutionLevel=requireAdministrator ://////=__= #AutoIt3Wrapper_Run_Obfuscator=n #AutoIt3Wrapper_Res_SaveSource=n #AutoIt3Wrapper_UseUpx=n #AutoIt3Wrapper_Run_After=upx.exe --best --compress-resources=1 "%out%" #include <array.au3> #include <date.au3> #NoTrayIcon Opt('MustDeclareVars', 1) Opt("TrayIconHide", 1) TraySetState(2) Const $ver = "v2.0" Const $build = "beta 2008-10-13" Const $coder = "UEZ" Const $appname = "SIC2.exe" If @ScriptName <> $appname Then ;force the scriptname to be SIC2.exe FileMove(@ScriptName, @ScriptDir & "" & $appname, 0) If @error = 0 Then ConsoleWrite(@CRLF & "Error renaming " & @ScriptName & " to SIC2.exe. Terminating..." & @CRLF & @CRLF) Exit EndIf EndIf Global $master, $slaves_threshold, $slaves, $slave Global $yy, $end, $parameters, $cs, $pid_sum, $errors, $list_end Global $filepath, $hFile, $aFile Global $os, $rl, $sv, $system, $timer, $kill_threshold, $prio Global $colItems, $colItems2, $objItem, $objReg, $objWMIService, $objWMILocator Global $path = @ScriptDir & "Output" Dim $ServerArray_tmp[1], $ServerArray[1] $slaves_threshold = 100 ;max. threads to be created at once $list_end = False If $CmdLine[0] = 0 Then Help() ;if no parameter is given call help Check_CMD_Parameter() ;check paramters If $master = 1 Then ;if master is set then prepare for master function call Build_List() Dim $PIDs[$slaves] ;create $PIDs array to save PID of each process $yy = 0 For $yy = 0 To UBound($PIDs) - 1 ;set all PIDs to 0 $PIDs[$yy] = 0 Next EndIf If $slaves > UBound($ServerArray) Then ;check whether n parameter is more than amount of systems ConsoleWrite("Error: more slaves (" & $slaves & ") selected then amount of system(s) (" & UBound($ServerArray) & ") is available!" & @CRLF & @CRLF) If UBound($ServerArray) = 1 Then $master = 0 $slave = 0 ConsoleWrite("But found only 1 system. ") Else Exit EndIf EndIf If $master = 1 And $slaves > 1 Then ;if parameter is master then call master function ConsoleWrite(@CRLF & "Starting parallel runs!" & @CRLF & @CRLF) Master() ElseIf $master = 0 And $slave = 1 Then ;if parameter is slave then call slave function. Slave function can only be called by master Slave() Else ;start sequential run ConsoleWrite(@CRLF & "Starting sequential run!" & @CRLF & @CRLF) Sequential_Run() EndIf Exit Func Master() ;master function controll multi threading processes Local $x Local $EndTime, $dsec, $dhr, $dmin Local $StartTime = _NowCalc() Local $t = Round(LN($slaves) * 1000, 0) $end = UBound($ServerArray) - 1 ;set end of operation to end of server list $kill_threshold = 60 ;when all systems has been started wait for process to kill in seconds $cs = 0 Do Create_PIDs() ;create processes regarding slaves parameter Sleep($t) Check_PIDs() ;check whether process is still existing ConsoleWrite($cs & "/" & UBound($ServerArray) & " (" & Round($cs / UBound($ServerArray) * 100, 2) & "% / " & _DateDiff('s', $StartTime, _NowCalc()) & " s) system(s) processed. " & @CR) Until $pid_sum = 0 ;if PID sum = 0 then all opened processes are closed Cleanup_Files() $EndTime = _NowCalc() $dsec = _DateDiff('s', $StartTime, $EndTime) $dmin = Round($dsec / 60, 2) $dhr = Round($dsec / 60 / 60, 2) ConsoleWrite(@CRLF & @CRLF & @CRLF & "Runtime: " & $dsec & " seconds (" & $dmin & "min / " & $dhr & "h) " & @CRLF & @CRLF) EndFunc ;==>Master Func Slave() ;slave function calls the sub functions depending on given parameters Local $function_name = "WMI general connection", $TimeStamp, $err Local $filename_error_current = $function_name & "_" & $system & "_error.log" Ping($system) If @error = 0 Then ProcessSetPriority(@AutoItPID, $prio) ;set process for sequential run to idle ;http://msdn2.microsoft.com/en-us/library/aa393720.aspx or [url="http://msdn2.microsoft.com/en-us/library/a...257%28VS.85%29.aspx"]http://msdn2.microsoft.com/en-us/library/a...257(VS.85).aspx[/url] ;$objWMILocator.ConnectServer(strServer = ".", strNamespace = "", strUser = "", strPassword = "", strLocale = "", strAuthority = "", iSecurityFlags = 0, objwbemNamedValueSet = 0) $function_name = "WMI general connection" $objWMIService = ObjGet("winmgmts:{impersonationLevel=impersonate}!" & $system & "rootcimv2") $err = @error If $err = 0 Then If $os = 1 Then OS($system) If $rl = 1 Then Role($system) If $sv = 1 Then Services($system) Else $TimeStamp = @MDAY & "." & @MON & "." & @YEAR & ";" & @HOUR & ":" & @MIN & ":" & @SEC FileWriteLine($path & $filename_error_current, $system & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;WMI error " & $err & " on server & " & $system & ";0;") EndIf Else $TimeStamp = @MDAY & "." & @MON & "." & @YEAR & ";" & @HOUR & ":" & @MIN & ":" & @SEC FileWriteLine($path & $filename_error_current, $system & ";" & $TimeStamp & ";" & $appname & ";Unreachable;Error;Cannot ping system " & $system & ";0;") EndIf EndFunc ;==>Slave Func Sequential_Run() ;starts getting information from systems one by one Local $function_name = "WMI general connection", $TimeStamp Local $filename_error_current Local $i, $err Local $EndTime, $dsec, $dhr, $dmin Local $StartTime = _NowCalc() Local $end = UBound($ServerArray) - 1 Local $chk_seq_run $chk_seq_run = Run($appname & " -chk_seq_run -pid " & @AutoItPID, "", @SW_HIDE) ;run sub process which checks current process whether it still runs after x seconds -> see function Chk_Seq_Run ProcessSetPriority(@AutoItPID, $prio) ;set current process to idle $slave = 0 For $i = 0 To $end $system = $ServerArray[$i] Ping($system) ;check system whether it is reachable via ping If @error = 0 Then $filename_error_current = $function_name & "_" & $system & "_error.log" $function_name = "WMI general connection" $objWMIService = ObjGet("winmgmts:{impersonationLevel=impersonate}!" & $system & "rootcimv2") $err = @error If $err = 0 Then If $os = 1 Then OS($system) If $rl = 1 Then Role($system) If $sv = 1 Then Services($system) Else $TimeStamp = @MDAY & "." & @MON & "." & @YEAR & ";" & @HOUR & ":" & @MIN & ":" & @SEC FileWriteLine($path & "Errors.csv", $system & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;WMI error " & $err & " on server & " & $system & ";0;") EndIf Else $TimeStamp = @MDAY & "." & @MON & "." & @YEAR & ";" & @HOUR & ":" & @MIN & ":" & @SEC FileWriteLine($path & "Errors.csv", $system & ";" & $TimeStamp & ";" & $appname & ";Unreachable;Error;Cannot ping system " & $system & ";0;") EndIf ConsoleWrite($i + 1 & "/" & ($end + 1) & " (" & Round(($i + 1) / ($end + 1) * 100, 2) & "% / " & _DateDiff('s', $StartTime, _NowCalc()) & " s) system(s) processed: " & $system & " " & @CR) ;output status Next If ProcessExists($chk_seq_run) Then ProcessClose($chk_seq_run); close sub process if exits $EndTime = _NowCalc() $dsec = _DateDiff('s', $StartTime, $EndTime) $dmin = Round($dsec / 60, 2) $dhr = Round($dsec / 60 / 60, 2) ConsoleWrite(@CRLF & @CRLF & @CRLF & "Runtime: " & $dsec & " seconds (" & $dmin & "min / " & $dhr & "h) " & @CRLF & @CRLF) Exit EndFunc ;==>Sequential_Run Func Chk_Seq_Run($pid) ;started by Sequentiell Run to check whether sequentiell run hangs Sleep(1000 * 60 * 60 * 3) ;should wait 3h until SIC2 will be terminated by this sub process If ProcessExists($pid) Then ConsoleWrite(@CRLF & $appname & " probably hangs! Terminating process (PID): " & $pid & " !!!" & @CRLF) ProcessClose($pid) EndIf Exit EndFunc ;==>Chk_Seq_Run Func Create_PIDs() ;created new processes Local $i If $list_end = False Then ;do not create any new process when system list end has been reached For $i = 0 To (UBound($PIDs) - 1) ;check PIDs array whether value is 0 If $PIDs[$i] = 0 And $cs <= $end Then ;found process slot to start another instance of program saving PID $PIDs[$i] = Run($appname & $parameters & " -prio " & $prio & " -slave -system " & $ServerArray[$cs], "", @SW_HIDE) ;call program with -slave parameter ProcessSetPriority($PIDs[$i], $prio) ;set the priority of process to default (idle) (0=idle, 1=below normal, 2=normal, 3=above normal, 4=high, 5=realtime) ;ConsoleWrite($cs & ")" & " PID " & $PIDs[$i] & ": " & $appname & $parameters & " -system " & $ServerArray[$cs] & " -slave created" & @CRLF) ;debug info $cs += 1 ;increase $cs -> system list pointer (get next system name from array) ;Sleep(100) EndIf Next If $cs = UBound($ServerArray) Then $list_end = True $timer = _NowCalc() EndIf EndIf EndFunc ;==>Create_PIDs Func Check_PIDs() ;checks whether created PIDs still exist Local $j $pid_sum = 0 For $j = 0 To (UBound($PIDs) - 1) ;check PIDs array $pid_sum += $PIDs[$j] ;sum all PID numbers If ProcessExists($PIDs[$j]) = 0 Then ;if PID doesn't exist anymore then set array to 0 to mark that slot is free for next program call ;If $PIDs[$j] > 0 Then ConsoleWrite("PID: " & $PIDs[$j] & " was closed." & @CRLF) ;debug info $PIDs[$j] = 0 ;if PID doesn't exist then set Array[$j] = 0 EndIf If $list_end And _DateDiff('s', $timer, _NowCalc()) >= $kill_threshold And $PIDs[$j] <> 0 Then ;when server list end has been reached then check for processes that are still running and kill them ConsoleWrite("Process time exceeded - killing PID " & $PIDs[$j] & "! " & @CRLF & @CRLF) ProcessClose($PIDs[$j]) ;kill process which is running more than 300 seconds $PIDs[$j] = 0 ;set array = 0 to get master process terminated. EndIf Next EndFunc ;==>Check_PIDs Func LN($x) Local $y Local $e = 2.71828182845904523536 $y = Log($x) / Log($e) Return $y EndFunc ;==>LN Func Check_CMD_Parameter() Local $x $master = 0 $slave = 0 $slaves = 0 $os = 0 $rl = 0 $sv = 0 $prio = 0 $parameters = "" If StringLower($CmdLine[1]) = StringLower("-?") Or StringLower($CmdLine[1]) = StringLower("/?") Or StringLower($CmdLine[1]) = StringLower("/help") Or StringLower($CmdLine[1]) = StringLower("-help") Then Help() EndIf If StringLower($CmdLine[1]) = StringLower("-chk_seq_run") And StringLower($CmdLine[2]) = StringLower("-pid") Then Chk_Seq_Run($CmdLine[3]) EndIf For $x = 1 To $CmdLine[0] If StringLower($CmdLine[$x]) = StringLower("-ver") Then Prog_Ver() If StringLower($CmdLine[$x]) = StringLower("-os") Then $os = 1 $parameters &= " -os" EndIf If StringLower($CmdLine[$x]) = StringLower("-rl") Then $rl = 1 $parameters &= " -rl" EndIf If StringLower($CmdLine[$x]) = StringLower("-sv") Then $sv = 1 $parameters &= " -sv" EndIf If StringLower($CmdLine[$x]) = StringLower("-system") Then $system = Find_Next_Parameter($x + 1) EndIf If StringLower($CmdLine[$x]) = StringLower("-master") Then $master = 1 If StringLower($CmdLine[$x]) = StringLower("-i") Then ;get input parameter to read in system names $filepath = Find_Next_Parameter($x + 1) EndIf If StringLower($CmdLine[$x]) = StringLower("-slaves") Then ;get amount of slaves $slaves = Find_Next_Parameter($x + 1) If StringIsInt($slaves) = 0 Then ConsoleWrite(@CRLF & "Argument for -slaves parameter is not integer!" & @CRLF & @CRLF) Exit EndIf $slave = 1 EndIf If StringLower($CmdLine[$x]) = StringLower("-slave") Then ;only for master call (internal) $slave = 1 EndIf If StringLower($CmdLine[$x]) = StringLower("-prio") Then ;get input parameter $prio = Find_Next_Parameter($x + 1) EndIf Next If $prio < 0 Or $prio > 3 Then $prio = 0 ;set prio to default (idle) if value is not between 0 and 3 If $os + $rl + $sv = 0 Then ConsoleWrite(@CRLF & "At least one argument is missing!" & @CRLF & @CRLF) Exit EndIf If $master = 1 And $slave = 0 Then ConsoleWrite(@CRLF & "Argument -slaves <n> is missing!" & @CRLF & @CRLF) Exit EndIf If $slaves > $slaves_threshold Then ConsoleWrite(@CRLF & "<n> from parameter -slaves <n> is over threshold (" & $slaves_threshold & ")!" & @CRLF & @CRLF) Exit EndIf If $master = 1 Then If $slaves < 1 Then ConsoleWrite(@CRLF & "Parameter -slaves < 1 makes no sense!" & @CRLF & @CRLF) Exit EndIf EndIf EndFunc ;==>Check_CMD_Parameter Func Find_Next_Parameter($StartIndex) Local $z, $parameter $parameter = "" For $z = $StartIndex To $CmdLine[0] If StringLeft($CmdLine[$z], 1) <> "-" Then If $z = $CmdLine[0] Then $parameter = $parameter & $CmdLine[$z] Else $parameter = $parameter & $CmdLine[$z] & " " EndIf Else ExitLoop EndIf Next ;If StringRight ($parameter, 1) = " " Then $parameter = StringTrimRight ($parameter, 1) Return StringStripWS($parameter, 8) EndFunc ;==>Find_Next_Parameter Func Help() ConsoleWrite(@CRLF & @CRLF) ConsoleWrite("Usage: " & $appname & @TAB & _ "[-i ""<system filename="" list="">""]" & @CRLF & @TAB & @TAB & _ "[-master -slaves <n>] for multiprocessing mode" & @CRLF & @TAB & @TAB & _ "[-ver] for version number" & @CRLF & @TAB & @TAB & _ "[-prio <0-3>] for process prio - standard is 0 (idle)" & @CRLF & @TAB & @TAB & _ "[<options>]" & @CRLF & @TAB & @TAB & _ "[-? | /? | -help | /help]] for this page" & @CRLF & @CRLF) ConsoleWrite("Options can be:" & @CRLF & @CRLF) ConsoleWrite(@TAB & "-os get only information about operating system" & @CRLF) ConsoleWrite(@TAB & "-rl get only information about role of system" & @CRLF) ConsoleWrite(@TAB & "-sv get only information about running services" & @CRLF & @CRLF) ConsoleWrite("If argument [-master -slaves <n>] is not given then sequentiell" & @CRLF & "mode will be used automatically!" & @CRLF & @CRLF) ConsoleWrite("Output will be written to folder Output where " & $appname & " was started." & @CRLF) ConsoleWrite("Output format is a semicolon separeted text file (*.csv)." & @CRLF & @CRLF) ConsoleWrite("On each run output files will be overwritten!" & @CRLF & @CRLF) Exit EndFunc ;==>Help Func Prog_Ver() ConsoleWrite(@CRLF & @CRLF & $appname & " " & $ver & " build " & $build & " by " & $coder & "." & @CRLF & @CRLF) ConsoleWrite($appname & " stands for S(ystem) I(nformation) C(ollector)." & @CRLF & @CRLF & @CRLF) Exit EndFunc ;==>Prog_Ver Func WMIDateStringToDate($dtmDate) Return (StringMid($dtmDate, 7, 2) & "." & _ StringMid($dtmDate, 5, 2) & "." & StringLeft($dtmDate, 4)); _ ;& " " & StringMid($dtmDate, 9, 2) & ":" & StringMid($dtmDate, 11, 2) & ":" & StringMid($dtmDate, 13, 2)) EndFunc ;==>WMIDateStringToDate Func Build_List() If FileExists($filepath) = 0 Then ConsoleWrite(@CRLF & "Unable to open file: " & $filepath & " !!!" & @CRLF & @CRLF) Exit EndIf ;_FileReadToArray function taken from File.au3 include $hFile = FileOpen($filepath, 0) ;open system list $aFile = FileRead($hFile, FileGetSize($filepath)) ;read system list to variable FileClose($hFile) If StringInStr($aFile, @LF) Then ;delete line feed and carriage return characters from string and write it to a temp array $ServerArray_tmp = StringSplit(StringStripCR($aFile), @LF) ElseIf StringInStr($aFile, @CR) Then ;; @LF does not exist so split on the @CR $ServerArray_tmp = StringSplit($aFile, @CR) Else ;; unable to split the file $ServerArray_tmp[0] = $aFile EndIf If @error > 0 Then ;terminate program on error ConsoleWrite(@CRLF & @CRLF & "Internal error (" & @error & ")! Aborting..." & @CRLF & @CRLF & @CRLF) Exit EndIf If UBound($ServerArray_tmp) - 1 > 0 Then $yy = 0 For $x = 1 To UBound($ServerArray_tmp) - 1 ;cleanup system list - only accept non empty lines If $ServerArray_tmp[$x] <> "" And StringIsASCII($ServerArray_tmp[$x]) = 1 Then ;save only ASCII and non empty strings ReDim $ServerArray[$yy + 1] $ServerArray[$yy] = $ServerArray_tmp[$x] $yy += 1 EndIf Next Else ReDim $ServerArray[1] $ServerArray[0] = $ServerArray_tmp[0] EndIf If FileExists($path & "OS.csv") Then FileDelete($path & "OS.csv") FileOpen($path & "OS.csv", 1 + 8) FileWrite($path & "OS.csv", "Systemname;OS Version;Service Pack Version;Bit;OS Install Date;EDS GoldDisk version" & @CRLF) If FileExists($path & "Role.csv") Then FileDelete($path & "Role.csv") FileOpen($path & "Role.csv", 1 + 8) FileWrite($path & "Role.csv", "Systemname;Domain;Domain Role;System Startup Options" & @CRLF) If FileExists($path & "Services.csv") Then FileDelete($path & "Services.csv") FileOpen($path & "Services.csv", 1 + 8) FileWrite($path & "Services.csv", "Systemname;Caption;Name;Start Mode;State;Starter Name" & @CRLF) If FileExists($path & "Errors.csv") Then FileDelete($path & "Errors.csv") FileOpen($path & "Errors.csv", 1 + 8) FileWrite($path & "Errors.csv", "Systemname;Date;Time;Application Name;Error Source;Error Level;Error Code" & @CRLF) EndFunc ;==>Build_List Func OS($srv) Local $array, $os_info Local $function_name = "OS" Local $OS_OperatingSystemVer, $OS_OperatingSystemSP, $OS_InstallationDate, $OS_OSArch, $OS_WinDir Local $HKLM, $BaseKey, $objReg, $OS_GoldDisk_Ver, $Key, $arrSubKeys, $SubKey Local $filename_error_current, $TimeStamp Local $filename_o = $function_name & "_" & $srv Local $filename_error_current = $function_name & "_" & $srv & "_error.log" $HKLM = 0x80000002 $BaseKey = "SOFTWAREMicrosoftWindows NTCurrentVersionWinlogon" $objReg = ObjGet("winmgmts:{impersonationLevel=impersonate}!" & $srv & "rootdefault:StdRegProv") If IsObj($objReg) Then ;$objReg.EnumKey ($HKLM, $BaseKey, $arrSubKeys) $OS_GoldDisk_Ver = "" $objReg.GetStringValue($HKLM, $BaseKey, "GoldDiskVersion", $OS_GoldDisk_Ver) Else $TimeStamp = @MDAY & "." & @MON & "." & @YEAR & ";" & @HOUR & ":" & @MIN & ":" & @SEC If $slave = 1 Then FileWriteLine($path & $filename_error_current, $srv & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;No WMI objects found for class StdRegProv on system " & $srv & ";0;") Else FileWriteLine($path & "Errors.csv", $srv & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;No WMI objects found for class StdRegProv on system " & $srv & ";0;") EndIf EndIf $colItems = $objWMIService.ExecQuery("SELECT Name, CSDVersion, InstallDate From Win32_OperatingSystem", "WQL", 0x30) $colItems2 = $objWMIService.ExecQuery("SELECT AddressWidth From Win32_Processor", "WQL", 0x30) If IsObj($colItems2) Then For $objItem In $colItems2 $OS_OSArch = $objItem.AddressWidth & "-bit" Next Else $TimeStamp = @MDAY & "." & @MON & "." & @YEAR & ";" & @HOUR & ":" & @MIN & ":" & @SEC If $slave = 1 Then FileWriteLine($path & $filename_error_current, $srv & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;No WMI objects found for class Win32_Processor on system " & $srv & ";0;") ElseIf $slave = 0 Then FileWriteLine($path & "Errors.csv", $srv & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;No WMI objects found for class Win32_Processor on system " & $srv & ";0;") EndIf EndIf If IsObj($colItems) Then For $objItem In $colItems $array = StringSplit($objItem.Name, "|", 1) $OS_OperatingSystemVer = $array[1] $OS_OperatingSystemSP = $objItem.CSDVersion $OS_InstallationDate = WMIDateStringToDate($objItem.InstallDate) Next $os_info = $srv & ";" & $OS_OperatingSystemVer & ";" & $OS_OperatingSystemSP & ";" & $OS_OSArch & ";" & $OS_InstallationDate & ";" & $OS_GoldDisk_Ver & ";" If $slave = 1 Then FileWriteLine($path & $filename_o & ".os", $os_info & @CRLF) ElseIf $slave = 0 Then FileWriteLine($path & "OS.csv", $os_info & @CRLF) EndIf Else $TimeStamp = @MDAY & "." & @MON & "." & @YEAR & ";" & @HOUR & ":" & @MIN & ":" & @SEC If $slave = 1 Then FileWriteLine($path & $filename_error_current, $srv & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;No WMI objects found for class Win32_OperatingSystem on system " & $srv & ";0;") ElseIf $slave = 0 Then FileWriteLine($path & "Errors.csv", $srv & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;No WMI objects found for class Win32_OperatingSystem on system " & $srv & ";0;") EndIf EndIf EndFunc ;==>OS Func Role($srv) Local $x, $filename_r, $filename_error_current, $TimeStamp, $function_name Local $Role_Domain, $Role_DomainRole, $Role_SystemStartupOptions, $Role $function_name = "Role" $filename_r = $function_name & "_" & $srv $filename_error_current = $function_name & "_" & $srv & "_error.log" $colItems = $objWMIService.ExecQuery("Select Domain, DomainRole, SystemStartupOptions from Win32_ComputerSystem", "WQL", 0x30) If IsObj($colItems) Then For $objItem In $colItems $Role_DomainRole = $objItem.DomainRole $Role_Domain = $objItem.Domain Select Case $Role_DomainRole = 0 $Role_DomainRole = "Standalone Workstation" Case $Role_DomainRole = 1 $Role_DomainRole = "Member Workstation" Case $Role_DomainRole = 2 $Role_DomainRole = "Standalone Server" Case $Role_DomainRole = 3 $Role_DomainRole = "Member Server" Case $Role_DomainRole = 4 $Role_DomainRole = "Backup Domain Controller" Case $Role_DomainRole = 5 $Role_DomainRole = "Primary Domain Controller" EndSelect For $x = 0 To UBound($objItem.SystemStartupOptions) - 1 $Role_SystemStartupOptions &= $objItem.SystemStartupOptions($x) & ", " Next Next $Role = $srv & ";" & $Role_Domain & ";" & $Role_DomainRole & ";" & $Role_SystemStartupOptions & ";" If $slave = 1 Then FileWriteLine($path & $filename_r & ".rl", $Role) ElseIf $slave = 0 Then FileWriteLine($path & "Role.csv", $Role) EndIf Else $TimeStamp = @MDAY & "." & @MON & "." & @YEAR & ";" & @HOUR & ":" & @MIN & ":" & @SEC If $slave = 1 Then FileWriteLine($path & $filename_error_current, $srv & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;No WMI objects found for class Win32_ComputerSystem on system " & $srv & ";0;") ElseIf $slave = 0 Then FileWriteLine($path & "Errors.csv", $srv & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;No WMI objects found for class Win32_ComputerSystem on system " & $srv & ";0;") EndIf EndIf EndFunc ;==>Role Func Services($srv) Local $function_name = "Services" Local $filename_error_current, $TimeStamp, $function_name Local $tmp Local $Service, $Service_Caption, $Service_Description, $Service_Name, $Service_StartMode, $Service_StartName, $Service_State, $Service_Status Local $filename_sv = $function_name & "_" & $srv $filename_error_current = $function_name & "_" & $srv & "_error.log" $colItems = $objWMIService.ExecQuery("Select Caption, Description, Name, StartMode, StartName, State, Status from Win32_Service", "WQL", 0x30) If IsObj($colItems) Then For $objItem In $colItems $Service_Caption = $objItem.Caption $Service_Name = $objItem.Name $Service_StartMode = $objItem.StartMode $Service_StartName = $objItem.StartName $Service_State = $objItem.State $Service &= $srv & ";" & $Service_Caption & ";" & $Service_Name & ";" & $Service_StartMode & ";" & $Service_State & ";" & $Service_StartName & ";" & @CRLF Next If $slave = 1 Then FileWriteLine($path & $filename_sv & ".sv", $Service) ElseIf $slave = 0 Then FileWriteLine($path & "Services.csv", $Service) EndIf Else $TimeStamp = @MDAY & "." & @MON & "." & @YEAR & ";" & @HOUR & ":" & @MIN & ":" & @SEC If $slave = 1 Then FileWriteLine($path & $filename_error_current, $srv & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;No WMI objects found for class Win32_Service on system " & $srv & ";0;") ElseIf $slave = 0 Then FileWriteLine($path & "Errors.csv", $srv & ";" & $TimeStamp & ";" & $appname & ";WMI;Error;No WMI objects found for class Win32_Service on system " & $srv & ";0;") EndIf EndIf EndFunc ;==>Services Func Cleanup_Files() ;merge files which were created with master / slave parameter call Dim $arr_role[1], $arr_os[1], $arr_services[1], $arr_error_logs[1] Local $search_os, $search_role, $search_services, $search_error_logs, $x, $path, $content $path = @ScriptDir & "Output" $search_role = FileFindFirstFile($path & "Role_*.rl") $search_os = FileFindFirstFile($path & "OS_*.os") $search_services = FileFindFirstFile($path & "Services_*.sv") $search_error_logs = FileFindFirstFile($path & "*_error.log") $x = 0 FileFindFirstFile($search_os) While 1 ReDim $arr_os[$x + 1] $arr_os[$x] = FileFindNextFile($search_os) If @error Then ExitLoop $x += 1 WEnd FileClose($search_os) $x = 0 FileFindFirstFile($search_role) While 1 ReDim $arr_role[$x + 1] $arr_role[$x] = FileFindNextFile($search_role) If @error Then ExitLoop $x += 1 WEnd FileClose($search_role) $x = 0 FileFindFirstFile($search_services) While 1 ReDim $arr_services[$x + 1] $arr_services[$x] = FileFindNextFile($search_services) If @error Then ExitLoop $x += 1 WEnd FileClose($search_services) $x = 0 FileFindFirstFile($search_error_logs) While 1 ReDim $arr_error_logs[$x + 1] $arr_error_logs[$x] = FileFindNextFile($search_error_logs) If @error Then ExitLoop $x += 1 WEnd FileClose($search_error_logs) For $x = 0 To UBound($arr_os) - 1 $content = FileRead($path & $arr_os[$x]) If @error = 0 Then If $content <> "" Then FileWrite($path & "OS.csv", $content) FileDelete($path & $arr_os[$x]) EndIf EndIf Next FileClose($path & "OS.csv") For $x = 0 To UBound($arr_role) - 1 $content = FileRead($path & $arr_role[$x]) If @error = 0 Then If $content <> "" Then FileWrite($path & "Role.csv", $content) FileDelete($path & $arr_role[$x]) EndIf EndIf Next FileClose($path & "Role.csv") For $x = 0 To UBound($arr_services) - 1 $content = FileRead($path & $arr_services[$x]) If @error = 0 Then If $content <> "" Then FileWrite($path & "Services.csv", $content) FileDelete($path & $arr_services[$x]) EndIf EndIf Next FileClose($path & "Services.csv") For $x = 0 To UBound($arr_error_logs) - 1 $content = FileRead($path & $arr_error_logs[$x]) If @error = 0 Then If $content <> "" Then FileWrite($path & "Errors.csv", $content) FileDelete($path & $arr_error_logs[$x]) EndIf EndIf Next FileClose($path & "Errors.csv") EndFunc ;==>Cleanup_FilesNew Version Below:You can download source + compiled exes (22 modules) here: SIC2.exe + SIC2.au3 + SIC2GUI.exeSource code of full version (22 modules) here (build 2012-02-24 beta): SIC2_Src.7z (149 downloads of previous versions)Use e.g. 7-Zip to extract archive.SIC2.txt Commandline Parameters:expandcollapse popupSIC2 v2.0 build 2010-06-08 beta by UEZ. Usage: SIC2 <-i (system list filename)> [-master] for multi processing mode [-slaves <n>] can only be used with -master parameter. Default is 20. [-prio <0-3>] for process prio - standard is 0 (idle) [-silent] no output to CMD [-no_folder] doesn't create the output folder. [-zip] move Run_YYYY-MM-DD_HHMMSS folder to ZIP archive [-shuffle] shuffels system list <options> see below! [-ver] for version number [-? | /? | -help | /help]] for this page Options can be (at least one option below must be set): -all activate all options below -di get only information about domain -dp get only information about disk partitions -el get only information about eventlog -ee [d] [id] get only information about eventlog system errors-> slow -hd get only information about harddisk -hw get only information about hardware -ls get only information about local shares -lu get only information about local user accounts -ms get only information about membership of local administrators group -nw get only information about network settings -od get only information about optical drive -os get only information about operating system -pc get only information about patches -pf get only information about pagefile -ph get only information about PnP hardware problem -pt get only information about printer -rl get only information about role of system -rt get only information about persistent IP4 routing table -sv get only information about services -sw get only information about installed software -tz get only information about timezone -vc get only information about video controller If you set only -ee then all errors of last 7 days will be enumerated! Otherwise set parameter [d] for days and [id] for event id. If argument [-master -slaves <n>] is not given then sequential mode will be used automatically! Each slave process takes around 6 MB of memory! System list file for parameter -i may contain only one name or ip address in each line of the appropriate system. Output will be written to folder Output where SIC2.exe was started and to a separate folder named Run_YYYY-MM-DD_HHMMSS. Output format is a semicolon separeted text file (*.csv) in Ouput's subfolder. Prio parameter can be from 0-3: Idle, Below Normal, Normal and Above Normal Examples: Parallel run: SIC2.exe -i list.txt -all -zip -master -slaves 25 SIC2.exe -i list.txt -os -nw -hw -master -slaves 15 SIC2.exe -i list.txt -ee 5 7000 -di -sw -od -master -prio 1 Sequential run: SIC2.exe -i list.txt -all SIC2.exe -i list.txt -el -pc -tz -ms -od -rt -zip SIC2.exe -i list.txt -ee 7 -sw -prio 2If I have more time I will code a GUI for SIC2 because without a GUI many people will not use it!-> Done! SIC2 GUI download here: SIC2_GUI.7z (10 downloads of previous versions)Please post any criticism, if you like it or not!UEZ Edited February 24, 2012 by UEZ Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
UEZ Posted October 13, 2008 Author Share Posted October 13, 2008 Sorry, a lot of stupid bugs posted. Above the version with less bugs and post renewed! UEZ Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
ptrex Posted October 14, 2008 Share Posted October 14, 2008 @UEZ Very nice !! It runs realy fast I must say. Regards, ptrex Contributions :Firewall Log Analyzer for XP - Creating COM objects without a need of DLL's - UPnP support in AU3Crystal Reports Viewer - PDFCreator in AutoIT - Duplicate File FinderSQLite3 Database functionality - USB Monitoring - Reading Excel using SQLRun Au3 as a Windows Service - File Monitor - Embedded Flash PlayerDynamic Functions - Control Panel Applets - Digital Signing Code - Excel Grid In AutoIT - Constants for Special Folders in WindowsRead data from Any Windows Edit Control - SOAP and Web Services in AutoIT - Barcode Printing Using PS - AU3 on LightTD WebserverMS LogParser SQL Engine in AutoIT - ImageMagick Image Processing - Converter @ Dec - Hex - Bin -Email Address Encoder - MSI Editor - SNMP - MIB ProtocolFinancial Functions UDF - Set ACL Permissions - Syntax HighLighter for AU3ADOR.RecordSet approach - Real OCR - HTTP Disk - PDF Reader Personal Worldclock - MS Indexing Engine - Printing ControlsGuiListView - Navigation (break the 4000 Limit barrier) - Registration Free COM DLL Distribution - Update - WinRM SMART Analysis - COM Object Browser - Excel PivotTable Object - VLC Media Player - Windows LogOnOff Gui -Extract Data from Outlook to Word & Excel - Analyze Event ID 4226 - DotNet Compiler Wrapper - Powershell_COM - New Link to comment Share on other sites More sharing options...
UEZ Posted November 24, 2008 Author Share Posted November 24, 2008 I discovered coincidentally that also TrendMicro has a tool with the name SIC. Should I rename my tools, in order to avoid legal problems? Did someone already make such experiences?Thanks,UEZ Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
ricky Posted November 17, 2011 Share Posted November 17, 2011 Hello, thanks for this software, I try to run it, but I have always the file is empty, why? Link to comment Share on other sites More sharing options...
UEZ Posted November 17, 2011 Author Share Posted November 17, 2011 (edited) Wow, after nearly 3 years a reply! How did you run it? In the CMD line or via the GUI? Is there a Errors.csv file in the output folder? If yes, what is the content? I need more information otherwise I cannot say what might be wrong! Br, UEZ Edited November 17, 2011 by UEZ Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
ricky Posted November 17, 2011 Share Posted November 17, 2011 Yes, because i'm looking for a script to give me the serial number of the HDD, and there is nothing in the forum... I execute by the GUi, and with the command line, I have the same problem : I execute this : SIC2.exe -i list.txt -all And in the prompt I have : File list.txt is empty! No csv file. Link to comment Share on other sites More sharing options...
UEZ Posted November 17, 2011 Author Share Posted November 17, 2011 (edited) What is the content of list.txt? list.txt must contain ip addresses or host names (one per line)! Further, as far as I can remember there is no option to read the s/n of the HDD but maybe I can add it... Br, UEZ Edited November 17, 2011 by UEZ Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
ricky Posted November 17, 2011 Share Posted November 17, 2011 Ok, sory but I didn't found the info about the content of this file. For the s/n, there is nothing, but you have a function? Link to comment Share on other sites More sharing options...
UEZ Posted November 17, 2011 Author Share Posted November 17, 2011 (edited) Try this: expandcollapse popupGlobal $oErrorHandler = ObjEvent("AutoIt.Error", "ObjErrorHandler") MsgBox(0, "Test", WMI_GetHDDSN(".")) Func WMI_GetHDDSN($host, $usr = "", $pass = "") ;coded by UEZ 2011 If $host = "." Then $host = "localhost" Local $HDD_SN Local $ping = Ping($host, 1000) If @error Then Return SetError(1, 0, -1) Local $objWMILocator = ObjCreate("WbemScripting.SWbemLocator") Local $objWMIService = $objWMILocator.ConnectServer($host, "rootcimv2", $usr, $pass, "", "", 128) If @error Then Return SetError(2, 0, -1) Local $colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_DiskDrive WHERE MediaType LIKE '%Fixed%'", "WQL", 0x30) If IsObj($colItems) Then For $objItem In $colItems $HDD_SN &= "Model: " & $objItem.Model & ", S/N: " & WMI_GetHDDSN2(StringMid($objItem.Name, 5), $host, $usr, $pass) & @LF Next Else Return SetError(3, 0, -1) EndIf Return $HDD_SN EndFunc Func WMI_GetHDDSN2($Tag, $host, $usr = "", $pass = "") ;coded by UEZ 2011 Local $HDD_SN Local $objWMILocator = ObjCreate("WbemScripting.SWbemLocator") Local $objWMIService = $objWMILocator.ConnectServer($host, "rootcimv2", $usr, $pass, "", "", 128) Local $colItems = $objWMIService.ExecQuery("SELECT SerialNumber,Tag FROM Win32_PhysicalMedia WHERE Tag LIKE '%" & $Tag & "%'", "WQL", 0x30) If IsObj($colItems) Then For $objItem In $colItems ConsoleWrite($objItem.Tag & @LF) $HDD_SN = $objItem.SerialNumber Next Else Return SetError(3, 0, -1) EndIf Return $HDD_SN EndFunc Func ObjErrorHandler() ConsoleWrite( "A COM Error has occured!" & @CRLF & @CRLF & _ "err.description is: " & @TAB & $oErrorHandler.description & @CRLF & _ "err.windescription:" & @TAB & $oErrorHandler & @CRLF & _ "err.number is: " & @TAB & Hex($oErrorHandler.number, 8) & @CRLF & _ "err.lastdllerror is: " & @TAB & $oErrorHandler.lastdllerror & @CRLF & _ "err.scriptline is: " & @TAB & $oErrorHandler.scriptline & @CRLF & _ "err.source is: " & @TAB & $oErrorHandler.source & @CRLF & _ "err.helpfile is: " & @TAB & $oErrorHandler.helpfile & @CRLF & _ "err.helpcontext is: " & @TAB & $oErrorHandler.helpcontext & @CRLF _ ) EndFunc Br, UEZ Edited November 17, 2011 by UEZ Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
ricky Posted November 17, 2011 Share Posted November 17, 2011 (edited) I try it on 3 different computers (XP-SP3), the same result, nothing in the msgbox,A COM Error has occured!err.description is: err.windescription: err.number is: 80041017err.lastdllerror is: 0err.scriptline is: 15err.source is: err.helpfile is: err.helpcontext is: Edited November 17, 2011 by ricky03 Link to comment Share on other sites More sharing options...
SkinnyWhiteGuy Posted November 17, 2011 Share Posted November 17, 2011 Local $objWMIService = $objWMILocator.ConnectServer($host, "rootcimv2", $usr, $pass, "", "", "&H80") This might need to be: Local $objWMIService = $objWMILocator.ConnectServer($host, "rootcimv2", $usr, $pass, Default, Default, 128) &H80 is the way VBScript does Hex Numbers, so for AutoIt, it would be 0x80, which is the same number as 128. I've used it with success along with ConnectServer. Link to comment Share on other sites More sharing options...
UEZ Posted November 17, 2011 Author Share Posted November 17, 2011 I updated the code from post#10. Please try again! Br, UEZ Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
ricky Posted November 17, 2011 Share Posted November 17, 2011 @SkinnyWhiteGuy :I try with your line and I try also with "" at the place of Default and the problem is the same with the same error. Link to comment Share on other sites More sharing options...
ricky Posted November 17, 2011 Share Posted November 17, 2011 Better, No COM error, but not correct : Model: WDC WD2500SD-01KCC0, S/N: 4457572d4143374c323635373738 036 0 0 0 0 Model: WDC WD5002ABYS-01B1B0, S/N: Link to comment Share on other sites More sharing options...
UEZ Posted November 17, 2011 Author Share Posted November 17, 2011 This is what WMI returns! And what about this?: Global $oErrorHandler = ObjEvent("AutoIt.Error", "ObjErrorHandler") MsgBox(0, "Test", WMI_GetHDDSN(".")) Func WMI_GetHDDSN($host, $usr = "", $pass = "") ;coded by UEZ 2011 If $host = "." Then $host = "localhost" Local $HDD_SN Local $ping = Ping($host, 1000) If @error Then Return SetError(1, 0, -1) Local $objWMILocator = ObjCreate("WbemScripting.SWbemLocator") Local $objWMIService = $objWMILocator.ConnectServer($host, "rootcimv2", $usr, $pass, "", "", 128) If @error Then Return SetError(2, 0, -1) Local $colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_DiskDrive WHERE MediaType LIKE '%Fixed%'", "WQL", 0x30) If IsObj($colItems) Then For $objItem In $colItems $HDD_SN &= "Model: " & $objItem.Model & ", S/N: " & $objItem.SerialNumber & @LF Next Else Return SetError(3, 0, -1) EndIf Return $HDD_SN EndFunc Func ObjErrorHandler() ConsoleWrite( "A COM Error has occured!" & @CRLF & @CRLF & _ "err.description is: " & @TAB & $oErrorHandler.description & @CRLF & _ "err.windescription:" & @TAB & $oErrorHandler & @CRLF & _ "err.number is: " & @TAB & Hex($oErrorHandler.number, 8) & @CRLF & _ "err.lastdllerror is: " & @TAB & $oErrorHandler.lastdllerror & @CRLF & _ "err.scriptline is: " & @TAB & $oErrorHandler.scriptline & @CRLF & _ "err.source is: " & @TAB & $oErrorHandler.source & @CRLF & _ "err.helpfile is: " & @TAB & $oErrorHandler.helpfile & @CRLF & _ "err.helpcontext is: " & @TAB & $oErrorHandler.helpcontext & @CRLF _ ) EndFunc On my notebook this function return not the correct value but value is correct from post#10. Br, UEZ Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
ricky Posted November 17, 2011 Share Posted November 17, 2011 Here are :Model: WDC WD2500SD-01KCC0, S/N:Model: WDC WD5002ABYS-01B1B0, S/N:A COM Error has occured!err.description is: err.windescription: err.number is: 80020006err.lastdllerror is: 0err.scriptline is: 18err.source is: err.helpfile is: err.helpcontext is: Link to comment Share on other sites More sharing options...
UEZ Posted November 17, 2011 Author Share Posted November 17, 2011 WinXP doesn't have the SerialNumber method in Win32_DiskDrive class Br, UEZ Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
ricky Posted November 17, 2011 Share Posted November 17, 2011 If you want, I have a little software in C with source who give this information. Link to comment Share on other sites More sharing options...
UEZ Posted November 17, 2011 Author Share Posted November 17, 2011 (edited) I can speak German, Turkish and English but not C/C++ But I can try! I asume if you want to enumerate only on local PC, not remotely, then the s/n can be read out of a build-in DLL (e.g. kernel32.dll)! Br, UEZ Edited November 17, 2011 by UEZ Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now