github.com/elastic/gosigar@v0.14.3/sys/windows/syscall_windows_test.go (about)

     1  package windows
     2  
     3  import (
     4  	"encoding/binary"
     5  	"os"
     6  	"runtime"
     7  	"syscall"
     8  	"testing"
     9  	"unsafe"
    10  
    11  	"github.com/pkg/errors"
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  func TestGetProcessImageFileName(t *testing.T) {
    16  	h, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(syscall.Getpid()))
    17  	if err != nil {
    18  		t.Fatal(err)
    19  	}
    20  
    21  	filename, err := GetProcessImageFileName(h)
    22  	if err != nil {
    23  		t.Fatal(err)
    24  	}
    25  
    26  	t.Logf("GetProcessImageFileName: %v", filename)
    27  }
    28  
    29  func TestGetProcessMemoryInfo(t *testing.T) {
    30  	h, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(syscall.Getpid()))
    31  	if err != nil {
    32  		t.Fatal(err)
    33  	}
    34  	counters, err := GetProcessMemoryInfo(h)
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  
    39  	t.Logf("GetProcessMemoryInfo: ProcessMemoryCountersEx=%+v", counters)
    40  }
    41  
    42  func TestGetLogicalDriveStrings(t *testing.T) {
    43  	drives, err := GetLogicalDriveStrings()
    44  	if err != nil {
    45  		t.Fatal(err)
    46  	}
    47  
    48  	t.Logf("GetLogicalDriveStrings: %v", drives)
    49  }
    50  
    51  func TestGetDriveType(t *testing.T) {
    52  	drives, err := GetLogicalDriveStrings()
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  
    57  	for _, drive := range drives {
    58  		dt, err := GetDriveType(drive)
    59  		if err != nil {
    60  			t.Fatal(err)
    61  		}
    62  
    63  		t.Logf("GetDriveType: drive=%v, type=%v", drive, dt)
    64  	}
    65  }
    66  
    67  func TestGetSystemTimes(t *testing.T) {
    68  	idle, kernel, user, err := GetSystemTimes()
    69  	if err != nil {
    70  		t.Fatal(err)
    71  	}
    72  
    73  	t.Logf("GetSystemTimes: idle=%v, kernel=%v, user=%v", idle, kernel, user)
    74  }
    75  
    76  func TestGlobalMemoryStatusEx(t *testing.T) {
    77  	mem, err := GlobalMemoryStatusEx()
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  
    82  	t.Logf("GlobalMemoryStatusEx: %+v", mem)
    83  }
    84  
    85  func TestEnumProcesses(t *testing.T) {
    86  	pids, err := EnumProcesses()
    87  	if err != nil {
    88  		t.Fatal(err)
    89  	}
    90  
    91  	t.Logf("EnumProcesses: %v", pids)
    92  }
    93  
    94  func TestGetDiskFreeSpaceEx(t *testing.T) {
    95  	if runtime.GOOS == "windows" {
    96  		t.Skip("test is not running on latest windows versions. See https://github.com/elastic/gosigar/issues/172")
    97  		return
    98  	}
    99  	drives, err := GetLogicalDriveStrings()
   100  	if err != nil {
   101  		t.Fatal(err)
   102  	}
   103  
   104  	for _, drive := range drives {
   105  		dt, err := GetDriveType(drive)
   106  		if err != nil {
   107  			t.Fatal(err)
   108  		}
   109  
   110  		// Ignore CDROM drives. They return an error if the drive is emtpy.
   111  		if dt != DRIVE_CDROM {
   112  			free, total, totalFree, err := GetDiskFreeSpaceEx(drive)
   113  			if err != nil {
   114  				t.Fatal(err)
   115  			}
   116  			t.Logf("GetDiskFreeSpaceEx: %v, %v, %v", free, total, totalFree)
   117  		}
   118  	}
   119  }
   120  
   121  func TestGetFilesystemType(t *testing.T) {
   122  	drives, err := GetLogicalDriveStrings()
   123  	if err != nil {
   124  		t.Fatal(err)
   125  	}
   126  
   127  	for _, drive := range drives {
   128  		volumeType, err := GetFilesystemType(drive)
   129  		if err != nil {
   130  			t.Fatal(err)
   131  		}
   132  		t.Logf("GetFilesystemType: %v - %v", drive, volumeType)
   133  	}
   134  }
   135  
   136  func TestGetWindowsVersion(t *testing.T) {
   137  	ver := GetWindowsVersion()
   138  	assert.True(t, ver.Major >= 5)
   139  	t.Logf("GetWindowsVersion: %+v", ver)
   140  }
   141  
   142  func TestCreateToolhelp32Snapshot(t *testing.T) {
   143  	handle, err := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  	defer syscall.CloseHandle(syscall.Handle(handle))
   148  
   149  	// Iterate over the snapshots until our PID is found.
   150  	pid := uint32(syscall.Getpid())
   151  	for {
   152  		process, err := Process32Next(handle)
   153  		if errors.Cause(err) == syscall.ERROR_NO_MORE_FILES {
   154  			break
   155  		}
   156  		if err != nil {
   157  			t.Fatal(err)
   158  		}
   159  
   160  		t.Logf("CreateToolhelp32Snapshot: ProcessEntry32=%v", process)
   161  
   162  		if process.ProcessID == pid {
   163  			assert.EqualValues(t, syscall.Getppid(), process.ParentProcessID)
   164  			return
   165  		}
   166  	}
   167  
   168  	assert.Fail(t, "Snapshot not found for PID=%v", pid)
   169  }
   170  
   171  func TestNtQuerySystemProcessorPerformanceInformation(t *testing.T) {
   172  	cpus, err := NtQuerySystemProcessorPerformanceInformation()
   173  	if err != nil {
   174  		t.Fatal(err)
   175  	}
   176  
   177  	assert.Len(t, cpus, runtime.NumCPU())
   178  
   179  	for i, cpu := range cpus {
   180  		assert.NotZero(t, cpu.IdleTime)
   181  		assert.NotZero(t, cpu.KernelTime)
   182  		assert.NotZero(t, cpu.UserTime)
   183  
   184  		t.Logf("CPU=%v SystemProcessorPerformanceInformation=%v", i, cpu)
   185  	}
   186  }
   187  
   188  func TestNtQueryProcessBasicInformation(t *testing.T) {
   189  	h, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(syscall.Getpid()))
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  
   194  	info, err := NtQueryProcessBasicInformation(h)
   195  	if err != nil {
   196  		t.Fatal(err)
   197  	}
   198  	defer syscall.CloseHandle(h)
   199  	assert.EqualValues(t, os.Getpid(), info.UniqueProcessID)
   200  	assert.EqualValues(t, os.Getppid(), info.InheritedFromUniqueProcessID)
   201  
   202  	t.Logf("NtQueryProcessBasicInformation: %+v", info)
   203  }
   204  
   205  func TestGetTickCount64(t *testing.T) {
   206  	uptime, err := GetTickCount64()
   207  	if err != nil {
   208  		t.Fatal(err)
   209  	}
   210  	assert.NotZero(t, uptime)
   211  }
   212  
   213  func TestGetUserProcessParams(t *testing.T) {
   214  	h, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, false, uint32(syscall.Getpid()))
   215  	if err != nil {
   216  		t.Fatal(err)
   217  	}
   218  	info, err := NtQueryProcessBasicInformation(h)
   219  	if err != nil {
   220  		t.Fatal(err)
   221  	}
   222  	userProc, err := GetUserProcessParams(h, info)
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  	defer syscall.CloseHandle(h)
   227  	assert.NoError(t, err)
   228  	assert.EqualValues(t, os.Getpid(), info.UniqueProcessID)
   229  	assert.EqualValues(t, os.Getppid(), info.InheritedFromUniqueProcessID)
   230  	assert.NotEmpty(t, userProc.CommandLine)
   231  }
   232  
   233  func TestGetUserProcessParamsInvalidHandle(t *testing.T) {
   234  	var handle syscall.Handle
   235  	var info = ProcessBasicInformation{PebBaseAddress: uintptr(0)}
   236  	userProc, err := GetUserProcessParams(handle, info)
   237  	assert.EqualValues(t, err.Error(), "The handle is invalid.")
   238  	assert.Empty(t, userProc)
   239  }
   240  
   241  func TestReadProcessUnicodeString(t *testing.T) {
   242  	h, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, false, uint32(syscall.Getpid()))
   243  	if err != nil {
   244  		t.Fatal(err)
   245  	}
   246  	defer syscall.CloseHandle(h)
   247  	info, err := NtQueryProcessBasicInformation(h)
   248  	if err != nil {
   249  		t.Fatal(err)
   250  	}
   251  	userProc, err := GetUserProcessParams(h, info)
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	read, err := ReadProcessUnicodeString(h, &userProc.CommandLine)
   256  	if err != nil {
   257  		t.Fatal(err)
   258  	}
   259  	assert.NoError(t, err)
   260  	assert.NotEmpty(t, read)
   261  }
   262  
   263  const currentProcessHandle = syscall.Handle(^uintptr(0))
   264  
   265  func TestReadProcessUnicodeStringTerminator(t *testing.T) {
   266  	data := []byte{'H', 0, 'E', 0, 'L', 0, 'L', 0, 'O', 0, 0, 0}
   267  	for n := len(data); n >= 0; n-- {
   268  		us := UnicodeString{
   269  			Buffer: uintptr(unsafe.Pointer(&data[0])),
   270  			Size:   uint16(n),
   271  		}
   272  		read, err := ReadProcessUnicodeString(currentProcessHandle, &us)
   273  		if err != nil {
   274  			t.Fatal(err)
   275  		}
   276  		nRead := len(read)
   277  		// Strings must match
   278  		assert.True(t, nRead >= n)
   279  		assert.Equal(t, data[:n], read[:n])
   280  		// result is an array of uint16, can't have odd length.
   281  		assert.True(t, nRead&1 == 0)
   282  		// Must include a zero terminator at the end.
   283  		assert.True(t, nRead >= 2)
   284  		assert.Zero(t, read[nRead-1])
   285  		assert.Zero(t, read[nRead-2])
   286  	}
   287  }
   288  
   289  func TestReadProcessUnicodeStringInvalidHandle(t *testing.T) {
   290  	var handle syscall.Handle
   291  	var cmd = UnicodeString{Size: 5, MaximumLength: 400, Buffer: 400}
   292  	read, err := ReadProcessUnicodeString(handle, &cmd)
   293  	assert.EqualValues(t, err.Error(), "The handle is invalid.")
   294  	assert.Empty(t, read)
   295  }
   296  
   297  func TestByteSliceToStringSlice(t *testing.T) {
   298  	cmd := syscall.GetCommandLine()
   299  	b := make([]byte, unsafe.Sizeof(cmd))
   300  	binary.LittleEndian.PutUint16(b, *cmd)
   301  	hes, err := ByteSliceToStringSlice(b)
   302  	assert.NoError(t, err)
   303  	assert.NotEmpty(t, hes)
   304  }
   305  
   306  func TestByteSliceToStringSliceEmptyBytes(t *testing.T) {
   307  	b := make([]byte, 0)
   308  	cmd, err := ByteSliceToStringSlice(b)
   309  	assert.NoError(t, err)
   310  	assert.Empty(t, cmd)
   311  }
   312  
   313  func mkUtf16bytes(s string) []byte {
   314  	n := len(s)
   315  	b := make([]byte, n*2)
   316  	for idx, val := range s {
   317  		*(*uint16)(unsafe.Pointer(&b[idx*2])) = uint16(val)
   318  	}
   319  	return b
   320  }
   321  
   322  func TestByteSliceToStringSliceNotTerminated(t *testing.T) {
   323  	b := mkUtf16bytes("Hello World")
   324  	cmd, err := ByteSliceToStringSlice(b)
   325  	assert.NoError(t, err)
   326  	assert.Len(t, cmd, 2)
   327  	assert.Equal(t, "Hello", cmd[0])
   328  	assert.Equal(t, "World", cmd[1])
   329  }
   330  
   331  func TestByteSliceToStringSliceNotOddSize(t *testing.T) {
   332  	b := mkUtf16bytes("BAD")[:5]
   333  	cmd, err := ByteSliceToStringSlice(b)
   334  	assert.NoError(t, err)
   335  	assert.Len(t, cmd, 1)
   336  	// Odd character is dropped
   337  	assert.Equal(t, "BA", cmd[0])
   338  }
   339  
   340  func TestReadProcessMemory(t *testing.T) {
   341  	h, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, false, uint32(syscall.Getpid()))
   342  	if err != nil {
   343  		t.Fatal(err)
   344  	}
   345  	info, err := NtQueryProcessBasicInformation(h)
   346  	if err != nil {
   347  		t.Fatal(err)
   348  	}
   349  	pebSize := 0x20 + 8
   350  	if unsafe.Sizeof(uintptr(0)) == 4 {
   351  		pebSize = 0x10 + 8
   352  	}
   353  	defer syscall.CloseHandle(h)
   354  	peb := make([]byte, pebSize)
   355  	nRead, err := ReadProcessMemory(h, info.PebBaseAddress, peb)
   356  	assert.NoError(t, err)
   357  	assert.NotEmpty(t, nRead)
   358  	assert.EqualValues(t, nRead, uintptr(pebSize))
   359  }
   360  
   361  // A zero-byte read is a no-op, no error is returned.
   362  func TestReadProcessMemoryZeroByteRead(t *testing.T) {
   363  	peb := make([]byte, 0)
   364  	var h syscall.Handle
   365  	var address uintptr
   366  	nRead, err := ReadProcessMemory(h, address, peb)
   367  	assert.NoError(t, err)
   368  	assert.Empty(t, nRead)
   369  }
   370  
   371  func TestReadProcessMemoryInvalidHandler(t *testing.T) {
   372  	peb := make([]byte, 10)
   373  	var h syscall.Handle
   374  	var address uintptr
   375  	nRead, err := ReadProcessMemory(h, address, peb)
   376  	assert.Error(t, err)
   377  	assert.EqualValues(t, err.Error(), "The handle is invalid.")
   378  	assert.Empty(t, nRead)
   379  }
   380  
   381  func TestGetAccessPaths(t *testing.T) {
   382  	paths, err := GetAccessPaths()
   383  	if err != nil {
   384  		t.Fatal(err)
   385  	}
   386  	assert.NotEmpty(t, paths)
   387  	assert.True(t, len(paths) >= 1)
   388  }
   389  
   390  func TestGetVolumes(t *testing.T) {
   391  	paths, err := GetVolumes()
   392  	if err != nil {
   393  		t.Fatal(err)
   394  	}
   395  	assert.NotEmpty(t, paths)
   396  	assert.True(t, len(paths) >= 1)
   397  }
   398  
   399  func TestGetVolumePathsForVolume(t *testing.T) {
   400  	if runtime.GOOS == "windows" {
   401  		t.Skip("test is not running on latest windows versions. See https://github.com/elastic/gosigar/issues/172")
   402  		return
   403  	}
   404  	volumes, err := GetVolumes()
   405  	if err != nil {
   406  		t.Fatal(err)
   407  	}
   408  	assert.NotNil(t, volumes)
   409  	assert.True(t, len(volumes) >= 1)
   410  	volumePath, err := GetVolumePathsForVolume(volumes[0])
   411  	if err != nil {
   412  		t.Fatal(err)
   413  	}
   414  	assert.NotNil(t, volumePath)
   415  	assert.True(t, len(volumePath) >= 1)
   416  }