From dd3736da05caf65563f8b17093595efef7b5bce5 Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Wed, 12 Jun 2024 15:22:35 +0800 Subject: [PATCH] feat(backing encryption): backing image encryption support ref: longhorn/longhorn 7051 Signed-off-by: Jack Lin --- Dockerfile.dapper | 2 +- go.mod | 18 +- go.sum | 34 +- pkg/client/data_source_client.go | 5 +- pkg/client/sync_client.go | 58 +- pkg/crypto/crypto.go | 169 ++++ pkg/datasource/service.go | 34 +- pkg/sync/handler.go | 6 +- pkg/sync/router.go | 1 + pkg/sync/service.go | 67 ++ pkg/sync/sync_file.go | 272 ++++++- pkg/types/types.go | 32 +- pkg/util/util.go | 13 + vendor/github.com/c9s/goprocinfo/LICENSE | 21 + .../c9s/goprocinfo/linux/cpuinfo.go | 133 +++ .../github.com/c9s/goprocinfo/linux/disk.go | 26 + .../c9s/goprocinfo/linux/diskstat.go | 100 +++ .../c9s/goprocinfo/linux/interrupts.go | 56 ++ .../c9s/goprocinfo/linux/loadavg.go | 67 ++ .../c9s/goprocinfo/linux/meminfo.go | 97 +++ .../github.com/c9s/goprocinfo/linux/mounts.go | 49 ++ .../github.com/c9s/goprocinfo/linux/net_ip.go | 173 ++++ .../c9s/goprocinfo/linux/net_tcp.go | 82 ++ .../c9s/goprocinfo/linux/net_udp.go | 59 ++ .../c9s/goprocinfo/linux/net_unix.go | 72 ++ .../c9s/goprocinfo/linux/netstat.go | 174 ++++ .../c9s/goprocinfo/linux/network_stat.go | 73 ++ .../c9s/goprocinfo/linux/process.go | 62 ++ .../c9s/goprocinfo/linux/process_cmdline.go | 39 + .../c9s/goprocinfo/linux/process_io.go | 71 ++ .../c9s/goprocinfo/linux/process_pid.go | 54 ++ .../goprocinfo/linux/process_sched_stat.go | 46 ++ .../c9s/goprocinfo/linux/process_stat.go | 303 +++++++ .../c9s/goprocinfo/linux/process_statm.go | 61 ++ .../c9s/goprocinfo/linux/process_status.go | 331 ++++++++ .../github.com/c9s/goprocinfo/linux/snmp.go | 151 ++++ .../c9s/goprocinfo/linux/sockstat.go | 98 +++ .../github.com/c9s/goprocinfo/linux/stat.go | 106 +++ .../github.com/c9s/goprocinfo/linux/uptime.go | 43 + .../github.com/c9s/goprocinfo/linux/vmstat.go | 373 +++++++++ vendor/github.com/go-logr/logr/README.md | 73 +- vendor/github.com/go-logr/logr/context.go | 33 + .../github.com/go-logr/logr/context_noslog.go | 49 ++ .../github.com/go-logr/logr/context_slog.go | 83 ++ vendor/github.com/go-logr/logr/logr.go | 43 - .../go-logr/logr/{slogr => }/sloghandler.go | 98 ++- .../go-logr/logr/{slogr => }/slogr.go | 48 +- .../go-logr/logr/{slogr => }/slogsink.go | 24 +- vendor/github.com/go-ole/go-ole/.travis.yml | 8 + vendor/github.com/go-ole/go-ole/ChangeLog.md | 49 ++ vendor/github.com/go-ole/go-ole/LICENSE | 21 + vendor/github.com/go-ole/go-ole/README.md | 46 ++ vendor/github.com/go-ole/go-ole/appveyor.yml | 54 ++ vendor/github.com/go-ole/go-ole/com.go | 344 ++++++++ vendor/github.com/go-ole/go-ole/com_func.go | 174 ++++ vendor/github.com/go-ole/go-ole/connect.go | 192 +++++ vendor/github.com/go-ole/go-ole/constants.go | 153 ++++ vendor/github.com/go-ole/go-ole/error.go | 51 ++ vendor/github.com/go-ole/go-ole/error_func.go | 8 + .../github.com/go-ole/go-ole/error_windows.go | 24 + vendor/github.com/go-ole/go-ole/guid.go | 284 +++++++ .../go-ole/go-ole/iconnectionpoint.go | 20 + .../go-ole/go-ole/iconnectionpoint_func.go | 21 + .../go-ole/go-ole/iconnectionpoint_windows.go | 43 + .../go-ole/iconnectionpointcontainer.go | 17 + .../go-ole/iconnectionpointcontainer_func.go | 11 + .../iconnectionpointcontainer_windows.go | 25 + vendor/github.com/go-ole/go-ole/idispatch.go | 94 +++ .../go-ole/go-ole/idispatch_func.go | 19 + .../go-ole/go-ole/idispatch_windows.go | 202 +++++ .../github.com/go-ole/go-ole/ienumvariant.go | 19 + .../go-ole/go-ole/ienumvariant_func.go | 19 + .../go-ole/go-ole/ienumvariant_windows.go | 63 ++ .../github.com/go-ole/go-ole/iinspectable.go | 18 + .../go-ole/go-ole/iinspectable_func.go | 15 + .../go-ole/go-ole/iinspectable_windows.go | 72 ++ .../go-ole/go-ole/iprovideclassinfo.go | 21 + .../go-ole/go-ole/iprovideclassinfo_func.go | 7 + .../go-ole/iprovideclassinfo_windows.go | 21 + vendor/github.com/go-ole/go-ole/itypeinfo.go | 34 + .../go-ole/go-ole/itypeinfo_func.go | 7 + .../go-ole/go-ole/itypeinfo_windows.go | 21 + vendor/github.com/go-ole/go-ole/iunknown.go | 57 ++ .../github.com/go-ole/go-ole/iunknown_func.go | 19 + .../go-ole/go-ole/iunknown_windows.go | 58 ++ vendor/github.com/go-ole/go-ole/ole.go | 190 +++++ .../go-ole/go-ole/oleutil/connection.go | 100 +++ .../go-ole/go-ole/oleutil/connection_func.go | 10 + .../go-ole/oleutil/connection_windows.go | 58 ++ .../go-ole/go-ole/oleutil/go-get.go | 6 + .../go-ole/go-ole/oleutil/oleutil.go | 127 +++ vendor/github.com/go-ole/go-ole/safearray.go | 27 + .../go-ole/go-ole/safearray_func.go | 211 +++++ .../go-ole/go-ole/safearray_windows.go | 337 ++++++++ .../go-ole/go-ole/safearrayconversion.go | 140 ++++ .../go-ole/go-ole/safearrayslices.go | 33 + vendor/github.com/go-ole/go-ole/utility.go | 101 +++ vendor/github.com/go-ole/go-ole/variables.go | 15 + vendor/github.com/go-ole/go-ole/variant.go | 105 +++ .../github.com/go-ole/go-ole/variant_386.go | 11 + .../github.com/go-ole/go-ole/variant_amd64.go | 12 + .../github.com/go-ole/go-ole/variant_arm.go | 11 + .../github.com/go-ole/go-ole/variant_arm64.go | 13 + .../go-ole/go-ole/variant_date_386.go | 22 + .../go-ole/go-ole/variant_date_amd64.go | 20 + .../go-ole/go-ole/variant_date_arm.go | 22 + .../go-ole/go-ole/variant_date_arm64.go | 23 + .../go-ole/go-ole/variant_ppc64le.go | 12 + .../github.com/go-ole/go-ole/variant_s390x.go | 12 + vendor/github.com/go-ole/go-ole/vt_string.go | 58 ++ vendor/github.com/go-ole/go-ole/winrt.go | 99 +++ vendor/github.com/go-ole/go-ole/winrt_doc.go | 36 + .../longhorn/go-common-libs/exec/exec.go | 125 +++ .../longhorn/go-common-libs/io/file.go | 361 +++++++++ .../longhorn/go-common-libs/ns/crypto.go | 69 ++ .../longhorn/go-common-libs/ns/executor.go | 87 ++ .../longhorn/go-common-libs/ns/file.go | 282 +++++++ .../longhorn/go-common-libs/ns/filelock.go | 143 ++++ .../longhorn/go-common-libs/ns/joiner.go | 379 +++++++++ .../longhorn/go-common-libs/ns/misc.go | 23 + .../longhorn/go-common-libs/ns/sys.go | 99 +++ .../longhorn/go-common-libs/proc/proc.go | 210 +++++ .../longhorn/go-common-libs/sync/filelock.go | 50 ++ .../longhorn/go-common-libs/sys/sys.go | 129 +++ .../longhorn/go-common-libs/types/crypto.go | 16 + .../longhorn/go-common-libs/types/exec.go | 15 + .../longhorn/go-common-libs/types/file.go | 33 + .../longhorn/go-common-libs/types/misc.go | 3 + .../go-common-libs/types/namespace.go | 42 + .../longhorn/go-common-libs/types/proc.go | 12 + .../longhorn/go-common-libs/types/sys.go | 13 + .../longhorn/go-common-libs/utils/grpc.go | 17 + .../longhorn/go-common-libs/utils/misc.go | 121 +++ vendor/github.com/mitchellh/go-ps/.gitignore | 1 + vendor/github.com/mitchellh/go-ps/.travis.yml | 4 + vendor/github.com/mitchellh/go-ps/LICENSE.md | 21 + vendor/github.com/mitchellh/go-ps/README.md | 34 + vendor/github.com/mitchellh/go-ps/Vagrantfile | 43 + vendor/github.com/mitchellh/go-ps/process.go | 40 + .../mitchellh/go-ps/process_darwin.go | 138 ++++ .../mitchellh/go-ps/process_freebsd.go | 260 ++++++ .../mitchellh/go-ps/process_linux.go | 35 + .../mitchellh/go-ps/process_solaris.go | 96 +++ .../mitchellh/go-ps/process_unix.go | 95 +++ .../mitchellh/go-ps/process_windows.go | 119 +++ .../github.com/power-devops/perfstat/LICENSE | 23 + .../power-devops/perfstat/c_helpers.c | 159 ++++ .../power-devops/perfstat/c_helpers.h | 58 ++ .../power-devops/perfstat/config.go | 18 + .../power-devops/perfstat/cpustat.go | 98 +++ .../power-devops/perfstat/diskstat.go | 137 ++++ .../github.com/power-devops/perfstat/doc.go | 315 ++++++++ .../power-devops/perfstat/fsstat.go | 31 + .../power-devops/perfstat/helpers.go | 764 ++++++++++++++++++ .../power-devops/perfstat/lparstat.go | 26 + .../power-devops/perfstat/lvmstat.go | 72 ++ .../power-devops/perfstat/memstat.go | 84 ++ .../power-devops/perfstat/netstat.go | 117 +++ .../power-devops/perfstat/procstat.go | 75 ++ .../power-devops/perfstat/sysconf.go | 195 +++++ .../power-devops/perfstat/systemcfg.go | 635 +++++++++++++++ .../power-devops/perfstat/types_cpu.go | 186 +++++ .../power-devops/perfstat/types_disk.go | 176 ++++ .../power-devops/perfstat/types_fs.go | 195 +++++ .../power-devops/perfstat/types_lpar.go | 68 ++ .../power-devops/perfstat/types_lvm.go | 31 + .../power-devops/perfstat/types_memory.go | 101 +++ .../power-devops/perfstat/types_network.go | 163 ++++ .../power-devops/perfstat/types_process.go | 43 + .../power-devops/perfstat/uptime.go | 35 + vendor/github.com/shirou/gopsutil/v3/LICENSE | 61 ++ .../shirou/gopsutil/v3/common/env.go | 23 + .../shirou/gopsutil/v3/disk/disk.go | 98 +++ .../shirou/gopsutil/v3/disk/disk_aix.go | 50 ++ .../shirou/gopsutil/v3/disk/disk_aix_cgo.go | 76 ++ .../shirou/gopsutil/v3/disk/disk_aix_nocgo.go | 187 +++++ .../shirou/gopsutil/v3/disk/disk_darwin.go | 94 +++ .../gopsutil/v3/disk/disk_darwin_cgo.go | 45 ++ .../gopsutil/v3/disk/disk_darwin_nocgo.go | 14 + .../shirou/gopsutil/v3/disk/disk_fallback.go | 30 + .../shirou/gopsutil/v3/disk/disk_freebsd.go | 192 +++++ .../gopsutil/v3/disk/disk_freebsd_386.go | 63 ++ .../gopsutil/v3/disk/disk_freebsd_amd64.go | 66 ++ .../gopsutil/v3/disk/disk_freebsd_arm.go | 63 ++ .../gopsutil/v3/disk/disk_freebsd_arm64.go | 66 ++ .../shirou/gopsutil/v3/disk/disk_linux.go | 561 +++++++++++++ .../shirou/gopsutil/v3/disk/disk_netbsd.go | 152 ++++ .../gopsutil/v3/disk/disk_netbsd_amd64.go | 45 ++ .../gopsutil/v3/disk/disk_netbsd_arm64.go | 45 ++ .../shirou/gopsutil/v3/disk/disk_openbsd.go | 159 ++++ .../gopsutil/v3/disk/disk_openbsd_386.go | 38 + .../gopsutil/v3/disk/disk_openbsd_amd64.go | 36 + .../gopsutil/v3/disk/disk_openbsd_arm.go | 38 + .../gopsutil/v3/disk/disk_openbsd_arm64.go | 38 + .../gopsutil/v3/disk/disk_openbsd_riscv64.go | 40 + .../shirou/gopsutil/v3/disk/disk_solaris.go | 261 ++++++ .../shirou/gopsutil/v3/disk/disk_unix.go | 63 ++ .../shirou/gopsutil/v3/disk/disk_windows.go | 241 ++++++ .../shirou/gopsutil/v3/disk/iostat_darwin.c | 129 +++ .../shirou/gopsutil/v3/disk/iostat_darwin.h | 32 + .../gopsutil/v3/internal/common/binary.go | 637 +++++++++++++++ .../gopsutil/v3/internal/common/common.go | 464 +++++++++++ .../v3/internal/common/common_darwin.go | 66 ++ .../v3/internal/common/common_freebsd.go | 82 ++ .../v3/internal/common/common_linux.go | 353 ++++++++ .../v3/internal/common/common_netbsd.go | 66 ++ .../v3/internal/common/common_openbsd.go | 66 ++ .../v3/internal/common/common_unix.go | 62 ++ .../v3/internal/common/common_windows.go | 304 +++++++ .../gopsutil/v3/internal/common/endian.go | 10 + .../gopsutil/v3/internal/common/sleep.go | 21 + .../gopsutil/v3/internal/common/warnings.go | 30 + vendor/github.com/yusufpapurcu/wmi/LICENSE | 20 + vendor/github.com/yusufpapurcu/wmi/README.md | 6 + .../yusufpapurcu/wmi/swbemservices.go | 261 ++++++ vendor/github.com/yusufpapurcu/wmi/wmi.go | 603 ++++++++++++++ .../golang.org/x/sys/windows/registry/key.go | 205 +++++ .../x/sys/windows/registry/mksyscall.go | 9 + .../x/sys/windows/registry/syscall.go | 32 + .../x/sys/windows/registry/value.go | 386 +++++++++ .../sys/windows/registry/zsyscall_windows.go | 117 +++ .../apimachinery/pkg/util/runtime/runtime.go | 15 +- .../k8s.io/apimachinery/pkg/util/wait/loop.go | 55 +- .../k8s.io/apimachinery/pkg/util/wait/poll.go | 28 +- vendor/k8s.io/klog/v2/OWNERS | 4 +- vendor/k8s.io/klog/v2/contextual_slog.go | 31 + vendor/k8s.io/klog/v2/klog.go | 23 +- vendor/k8s.io/klog/v2/klogr_slog.go | 10 +- vendor/k8s.io/klog/v2/safeptr.go | 34 + vendor/k8s.io/mount-utils/mount_linux.go | 73 +- vendor/k8s.io/mount-utils/mount_windows.go | 6 +- vendor/modules.txt | 49 +- 232 files changed, 21992 insertions(+), 266 deletions(-) create mode 100644 pkg/crypto/crypto.go create mode 100644 vendor/github.com/c9s/goprocinfo/LICENSE create mode 100644 vendor/github.com/c9s/goprocinfo/linux/cpuinfo.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/disk.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/diskstat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/interrupts.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/loadavg.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/meminfo.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/mounts.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/net_ip.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/net_tcp.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/net_udp.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/net_unix.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/netstat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/network_stat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_cmdline.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_io.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_pid.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_sched_stat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_stat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_statm.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_status.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/snmp.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/sockstat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/stat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/uptime.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/vmstat.go create mode 100644 vendor/github.com/go-logr/logr/context.go create mode 100644 vendor/github.com/go-logr/logr/context_noslog.go create mode 100644 vendor/github.com/go-logr/logr/context_slog.go rename vendor/github.com/go-logr/logr/{slogr => }/sloghandler.go (63%) rename vendor/github.com/go-logr/logr/{slogr => }/slogr.go (66%) rename vendor/github.com/go-logr/logr/{slogr => }/slogsink.go (82%) create mode 100644 vendor/github.com/go-ole/go-ole/.travis.yml create mode 100644 vendor/github.com/go-ole/go-ole/ChangeLog.md create mode 100644 vendor/github.com/go-ole/go-ole/LICENSE create mode 100644 vendor/github.com/go-ole/go-ole/README.md create mode 100644 vendor/github.com/go-ole/go-ole/appveyor.yml create mode 100644 vendor/github.com/go-ole/go-ole/com.go create mode 100644 vendor/github.com/go-ole/go-ole/com_func.go create mode 100644 vendor/github.com/go-ole/go-ole/connect.go create mode 100644 vendor/github.com/go-ole/go-ole/constants.go create mode 100644 vendor/github.com/go-ole/go-ole/error.go create mode 100644 vendor/github.com/go-ole/go-ole/error_func.go create mode 100644 vendor/github.com/go-ole/go-ole/error_windows.go create mode 100644 vendor/github.com/go-ole/go-ole/guid.go create mode 100644 vendor/github.com/go-ole/go-ole/iconnectionpoint.go create mode 100644 vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go create mode 100644 vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go create mode 100644 vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go create mode 100644 vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go create mode 100644 vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go create mode 100644 vendor/github.com/go-ole/go-ole/idispatch.go create mode 100644 vendor/github.com/go-ole/go-ole/idispatch_func.go create mode 100644 vendor/github.com/go-ole/go-ole/idispatch_windows.go create mode 100644 vendor/github.com/go-ole/go-ole/ienumvariant.go create mode 100644 vendor/github.com/go-ole/go-ole/ienumvariant_func.go create mode 100644 vendor/github.com/go-ole/go-ole/ienumvariant_windows.go create mode 100644 vendor/github.com/go-ole/go-ole/iinspectable.go create mode 100644 vendor/github.com/go-ole/go-ole/iinspectable_func.go create mode 100644 vendor/github.com/go-ole/go-ole/iinspectable_windows.go create mode 100644 vendor/github.com/go-ole/go-ole/iprovideclassinfo.go create mode 100644 vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go create mode 100644 vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go create mode 100644 vendor/github.com/go-ole/go-ole/itypeinfo.go create mode 100644 vendor/github.com/go-ole/go-ole/itypeinfo_func.go create mode 100644 vendor/github.com/go-ole/go-ole/itypeinfo_windows.go create mode 100644 vendor/github.com/go-ole/go-ole/iunknown.go create mode 100644 vendor/github.com/go-ole/go-ole/iunknown_func.go create mode 100644 vendor/github.com/go-ole/go-ole/iunknown_windows.go create mode 100644 vendor/github.com/go-ole/go-ole/ole.go create mode 100644 vendor/github.com/go-ole/go-ole/oleutil/connection.go create mode 100644 vendor/github.com/go-ole/go-ole/oleutil/connection_func.go create mode 100644 vendor/github.com/go-ole/go-ole/oleutil/connection_windows.go create mode 100644 vendor/github.com/go-ole/go-ole/oleutil/go-get.go create mode 100644 vendor/github.com/go-ole/go-ole/oleutil/oleutil.go create mode 100644 vendor/github.com/go-ole/go-ole/safearray.go create mode 100644 vendor/github.com/go-ole/go-ole/safearray_func.go create mode 100644 vendor/github.com/go-ole/go-ole/safearray_windows.go create mode 100644 vendor/github.com/go-ole/go-ole/safearrayconversion.go create mode 100644 vendor/github.com/go-ole/go-ole/safearrayslices.go create mode 100644 vendor/github.com/go-ole/go-ole/utility.go create mode 100644 vendor/github.com/go-ole/go-ole/variables.go create mode 100644 vendor/github.com/go-ole/go-ole/variant.go create mode 100644 vendor/github.com/go-ole/go-ole/variant_386.go create mode 100644 vendor/github.com/go-ole/go-ole/variant_amd64.go create mode 100644 vendor/github.com/go-ole/go-ole/variant_arm.go create mode 100644 vendor/github.com/go-ole/go-ole/variant_arm64.go create mode 100644 vendor/github.com/go-ole/go-ole/variant_date_386.go create mode 100644 vendor/github.com/go-ole/go-ole/variant_date_amd64.go create mode 100644 vendor/github.com/go-ole/go-ole/variant_date_arm.go create mode 100644 vendor/github.com/go-ole/go-ole/variant_date_arm64.go create mode 100644 vendor/github.com/go-ole/go-ole/variant_ppc64le.go create mode 100644 vendor/github.com/go-ole/go-ole/variant_s390x.go create mode 100644 vendor/github.com/go-ole/go-ole/vt_string.go create mode 100644 vendor/github.com/go-ole/go-ole/winrt.go create mode 100644 vendor/github.com/go-ole/go-ole/winrt_doc.go create mode 100644 vendor/github.com/longhorn/go-common-libs/exec/exec.go create mode 100644 vendor/github.com/longhorn/go-common-libs/io/file.go create mode 100644 vendor/github.com/longhorn/go-common-libs/ns/crypto.go create mode 100644 vendor/github.com/longhorn/go-common-libs/ns/executor.go create mode 100644 vendor/github.com/longhorn/go-common-libs/ns/file.go create mode 100644 vendor/github.com/longhorn/go-common-libs/ns/filelock.go create mode 100644 vendor/github.com/longhorn/go-common-libs/ns/joiner.go create mode 100644 vendor/github.com/longhorn/go-common-libs/ns/misc.go create mode 100644 vendor/github.com/longhorn/go-common-libs/ns/sys.go create mode 100644 vendor/github.com/longhorn/go-common-libs/proc/proc.go create mode 100644 vendor/github.com/longhorn/go-common-libs/sync/filelock.go create mode 100644 vendor/github.com/longhorn/go-common-libs/sys/sys.go create mode 100644 vendor/github.com/longhorn/go-common-libs/types/crypto.go create mode 100644 vendor/github.com/longhorn/go-common-libs/types/exec.go create mode 100644 vendor/github.com/longhorn/go-common-libs/types/file.go create mode 100644 vendor/github.com/longhorn/go-common-libs/types/misc.go create mode 100644 vendor/github.com/longhorn/go-common-libs/types/namespace.go create mode 100644 vendor/github.com/longhorn/go-common-libs/types/proc.go create mode 100644 vendor/github.com/longhorn/go-common-libs/types/sys.go create mode 100644 vendor/github.com/longhorn/go-common-libs/utils/grpc.go create mode 100644 vendor/github.com/longhorn/go-common-libs/utils/misc.go create mode 100644 vendor/github.com/mitchellh/go-ps/.gitignore create mode 100644 vendor/github.com/mitchellh/go-ps/.travis.yml create mode 100644 vendor/github.com/mitchellh/go-ps/LICENSE.md create mode 100644 vendor/github.com/mitchellh/go-ps/README.md create mode 100644 vendor/github.com/mitchellh/go-ps/Vagrantfile create mode 100644 vendor/github.com/mitchellh/go-ps/process.go create mode 100644 vendor/github.com/mitchellh/go-ps/process_darwin.go create mode 100644 vendor/github.com/mitchellh/go-ps/process_freebsd.go create mode 100644 vendor/github.com/mitchellh/go-ps/process_linux.go create mode 100644 vendor/github.com/mitchellh/go-ps/process_solaris.go create mode 100644 vendor/github.com/mitchellh/go-ps/process_unix.go create mode 100644 vendor/github.com/mitchellh/go-ps/process_windows.go create mode 100644 vendor/github.com/power-devops/perfstat/LICENSE create mode 100644 vendor/github.com/power-devops/perfstat/c_helpers.c create mode 100644 vendor/github.com/power-devops/perfstat/c_helpers.h create mode 100644 vendor/github.com/power-devops/perfstat/config.go create mode 100644 vendor/github.com/power-devops/perfstat/cpustat.go create mode 100644 vendor/github.com/power-devops/perfstat/diskstat.go create mode 100644 vendor/github.com/power-devops/perfstat/doc.go create mode 100644 vendor/github.com/power-devops/perfstat/fsstat.go create mode 100644 vendor/github.com/power-devops/perfstat/helpers.go create mode 100644 vendor/github.com/power-devops/perfstat/lparstat.go create mode 100644 vendor/github.com/power-devops/perfstat/lvmstat.go create mode 100644 vendor/github.com/power-devops/perfstat/memstat.go create mode 100644 vendor/github.com/power-devops/perfstat/netstat.go create mode 100644 vendor/github.com/power-devops/perfstat/procstat.go create mode 100644 vendor/github.com/power-devops/perfstat/sysconf.go create mode 100644 vendor/github.com/power-devops/perfstat/systemcfg.go create mode 100644 vendor/github.com/power-devops/perfstat/types_cpu.go create mode 100644 vendor/github.com/power-devops/perfstat/types_disk.go create mode 100644 vendor/github.com/power-devops/perfstat/types_fs.go create mode 100644 vendor/github.com/power-devops/perfstat/types_lpar.go create mode 100644 vendor/github.com/power-devops/perfstat/types_lvm.go create mode 100644 vendor/github.com/power-devops/perfstat/types_memory.go create mode 100644 vendor/github.com/power-devops/perfstat/types_network.go create mode 100644 vendor/github.com/power-devops/perfstat/types_process.go create mode 100644 vendor/github.com/power-devops/perfstat/uptime.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/LICENSE create mode 100644 vendor/github.com/shirou/gopsutil/v3/common/env.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_aix.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_aix_cgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_aix_nocgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_darwin.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_darwin_cgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_darwin_nocgo.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_fallback.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_freebsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_freebsd_386.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_freebsd_amd64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_freebsd_arm.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_freebsd_arm64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_linux.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_netbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_netbsd_amd64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_netbsd_arm64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_openbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_openbsd_386.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_openbsd_amd64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_openbsd_arm.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_openbsd_arm64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_openbsd_riscv64.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_solaris.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_unix.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/disk_windows.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/iostat_darwin.c create mode 100644 vendor/github.com/shirou/gopsutil/v3/disk/iostat_darwin.h create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/binary.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_darwin.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_freebsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_linux.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_netbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_openbsd.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_unix.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/common_windows.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/endian.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/sleep.go create mode 100644 vendor/github.com/shirou/gopsutil/v3/internal/common/warnings.go create mode 100644 vendor/github.com/yusufpapurcu/wmi/LICENSE create mode 100644 vendor/github.com/yusufpapurcu/wmi/README.md create mode 100644 vendor/github.com/yusufpapurcu/wmi/swbemservices.go create mode 100644 vendor/github.com/yusufpapurcu/wmi/wmi.go create mode 100644 vendor/golang.org/x/sys/windows/registry/key.go create mode 100644 vendor/golang.org/x/sys/windows/registry/mksyscall.go create mode 100644 vendor/golang.org/x/sys/windows/registry/syscall.go create mode 100644 vendor/golang.org/x/sys/windows/registry/value.go create mode 100644 vendor/golang.org/x/sys/windows/registry/zsyscall_windows.go create mode 100644 vendor/k8s.io/klog/v2/contextual_slog.go create mode 100644 vendor/k8s.io/klog/v2/safeptr.go diff --git a/Dockerfile.dapper b/Dockerfile.dapper index 58800e86..86e2efab 100644 --- a/Dockerfile.dapper +++ b/Dockerfile.dapper @@ -29,4 +29,4 @@ RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/i ## Install Docker Buildx: The docker version in dapper is too old to have buildx. Install it manually. RUN curl -sSfLO https://github.com/docker/buildx/releases/download/v0.13.1/buildx-v0.13.1.linux-${ARCH} && \ chmod +x buildx-v0.13.1.linux-${ARCH} && \ - mv buildx-v0.13.1.linux-${ARCH} /usr/local/bin/buildx + mv buildx-v0.13.1.linux-${ARCH} /usr/local/bin/buildx \ No newline at end of file diff --git a/go.mod b/go.mod index e0457d4d..27c846f2 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/longhorn/backing-image-manager -go 1.22 +go 1.22.0 toolchain go1.22.4 @@ -9,6 +9,7 @@ require ( github.com/golang/protobuf v1.5.4 github.com/gorilla/mux v1.8.1 github.com/longhorn/backupstore v0.0.0-20240624084713-e98e31ebcebb + github.com/longhorn/go-common-libs v0.0.0-20240627075631-d78642cff5e1 github.com/longhorn/longhorn-engine v1.6.0-dev-20231217.0.20240418025706-519598108463 github.com/longhorn/sparse-tools v0.0.0-20240513025352-ed49dd3f93eb github.com/longhorn/types v0.0.0-20240624083620-f11ba48bf396 @@ -29,34 +30,39 @@ require ( github.com/aws/aws-sdk-go v1.34.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.12.0 // indirect + github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/gammazero/deque v0.2.1 // indirect github.com/gammazero/workerpool v1.1.3 // indirect - github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/jmespath/go-jmespath v0.3.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/longhorn/go-common-libs v0.0.0-20240411093823-b8862efb8e03 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/go-ps v1.0.0 // indirect github.com/moby/sys/mountinfo v0.6.2 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/pierrec/lz4/v4 v4.1.17 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_golang v1.15.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shirou/gopsutil/v3 v3.24.5 // indirect github.com/slok/goresilience v0.2.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect - k8s.io/apimachinery v0.27.1 // indirect - k8s.io/klog/v2 v2.110.1 // indirect - k8s.io/mount-utils v0.29.3 // indirect + k8s.io/apimachinery v0.30.2 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/mount-utils v0.30.2 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect ) diff --git a/go.sum b/go.sum index 5e7316fd..9dcc3835 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.12.0 h1:U/q1fAF7xXRhFCrhROzIfffYnu+dlS38vCZtmFVPHmA= github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g= +github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= @@ -31,8 +33,10 @@ github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0 github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU= github.com/gammazero/workerpool v1.1.3 h1:WixN4xzukFoN0XSeXF6puqEqFTl2mECI9S6W44HWy9Q= github.com/gammazero/workerpool v1.1.3/go.mod h1:wPjyBLDbyKnUn2XwwyD3EEwo9dHutia9/fwNmSHWACc= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= @@ -57,8 +61,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/longhorn/backupstore v0.0.0-20240624084713-e98e31ebcebb h1:equCPROzsJiPG94T4GkF78qROAIIEk107OAz3gUUJAI= github.com/longhorn/backupstore v0.0.0-20240624084713-e98e31ebcebb/go.mod h1:8+cn7oATMLHE8x8BUZ+U/HSn1odDHVbRgnsLSB43i/8= -github.com/longhorn/go-common-libs v0.0.0-20240411093823-b8862efb8e03 h1:RN7mq4FrbHcAeemI5tDha9u4X+RSRrPugD1cY1FHdvo= -github.com/longhorn/go-common-libs v0.0.0-20240411093823-b8862efb8e03/go.mod h1:7onp+E4hSg2DnB40dJU0Y7adrvykGg6jHxOb48imPGg= +github.com/longhorn/go-common-libs v0.0.0-20240627075631-d78642cff5e1 h1:VGSNK9AEL6r9UocxZ0LoFPv1mn/jcstEc3LDS3GedZk= +github.com/longhorn/go-common-libs v0.0.0-20240627075631-d78642cff5e1/go.mod h1:wpLEAlsDCnqBA7QfZg0gxYeR8MmLbWHbdidWYwnRbyM= github.com/longhorn/longhorn-engine v1.6.0-dev-20231217.0.20240418025706-519598108463 h1:KxddgUYC9InOhe8MxfbxzOL5v9q7f5DyThxQGGKvqVw= github.com/longhorn/longhorn-engine v1.6.0-dev-20231217.0.20240418025706-519598108463/go.mod h1:WNiZl2l51I36/c8dewxkyWd0yBA5Anznzki0knKO88U= github.com/longhorn/sparse-tools v0.0.0-20240513025352-ed49dd3f93eb h1:Kh89s6i5T1W6BT1Aq9W1YHXojbbcTXlDieWC5KWAs/E= @@ -68,6 +72,8 @@ github.com/longhorn/types v0.0.0-20240624083620-f11ba48bf396/go.mod h1:fonrC6SwG github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= @@ -80,6 +86,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= @@ -99,6 +107,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slok/goresilience v0.2.0 h1:dagdIiWlhTm7BK/r/LRKz+zvw0SCNk+nHf7obdsbzxQ= @@ -120,6 +130,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/urfave/cli v1.22.15 h1:nuqt+pdC/KqswQKhETJjo7pvn/k4xMUxgW6liI7XpnM= github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -136,8 +148,10 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -169,11 +183,11 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/apimachinery v0.27.1 h1:EGuZiLI95UQQcClhanryclaQE6xjg1Bts6/L3cD7zyc= -k8s.io/apimachinery v0.27.1/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM= -k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= -k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= -k8s.io/mount-utils v0.29.3 h1:iEcqPP7Vv8UClH8nnMfovtmy/04fIloRW9JuSXykoZ0= -k8s.io/mount-utils v0.29.3/go.mod h1:9IWJTMe8tG0MYMLEp60xK9GYVeCdA3g4LowmnVi+t9Y= +k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg= +k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/mount-utils v0.30.2 h1:2KDVY9hXyDyRw9EO4lmox4+Nn5atVOq+4ffZ/br2aAU= +k8s.io/mount-utils v0.30.2/go.mod h1:9sCVmwGLcV1MPvbZ+rToMDnl1QcGozy+jBPd0MsQLIo= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= diff --git a/pkg/client/data_source_client.go b/pkg/client/data_source_client.go index 14823d07..9998610c 100644 --- a/pkg/client/data_source_client.go +++ b/pkg/client/data_source_client.go @@ -11,6 +11,7 @@ import ( "github.com/longhorn/backing-image-manager/api" "github.com/longhorn/backing-image-manager/pkg/util" + "github.com/pkg/errors" ) type DataSourceClient struct { @@ -72,7 +73,7 @@ func (client *DataSourceClient) Transfer() error { bodyContent, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("%s, failed to read the response body: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), err) + return errors.Wrapf(err, "%v, failed to read the response body", util.GetHTTPClientErrorPrefix(resp.StatusCode)) } if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound { return fmt.Errorf("%s or http.StatusNotFound(%d), response body content: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), http.StatusNotFound, string(bodyContent)) @@ -128,7 +129,7 @@ func (client *DataSourceClient) Upload(filePath string) error { bodyContent, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("%s, failed to read the response body: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), err) + return errors.Wrapf(err, "%v, failed to read the response body", util.GetHTTPClientErrorPrefix(resp.StatusCode)) } if resp.StatusCode != http.StatusOK { return fmt.Errorf("%s, response body content: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), string(bodyContent)) diff --git a/pkg/client/sync_client.go b/pkg/client/sync_client.go index a0a75081..5178e9ed 100644 --- a/pkg/client/sync_client.go +++ b/pkg/client/sync_client.go @@ -113,7 +113,7 @@ func (client *SyncClient) Delete(filePath string) error { bodyContent, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("%s, failed to read the response body: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), err) + return errors.Wrapf(err, "%v, failed to read the response body", util.GetHTTPClientErrorPrefix(resp.StatusCode)) } if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound { return fmt.Errorf("%s or http.StatusNotFound(%d), response body content: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), http.StatusNotFound, string(bodyContent)) @@ -142,7 +142,7 @@ func (client *SyncClient) Forget(filePath string) error { bodyContent, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("%s, failed to read the response body: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), err) + return errors.Wrapf(err, "%v, failed to read the response body", util.GetHTTPClientErrorPrefix(resp.StatusCode)) } if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound { return fmt.Errorf("%s or http.StatusNotFound(%d), response body content: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), http.StatusNotFound, string(bodyContent)) @@ -177,7 +177,7 @@ func (client *SyncClient) Fetch(srcFilePath, dstFilePath, uuid, diskUUID, expect bodyContent, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("%s, failed to read the response body: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), err) + return errors.Wrapf(err, "%v, failed to read the response body", util.GetHTTPClientErrorPrefix(resp.StatusCode)) } if resp.StatusCode != http.StatusOK { return fmt.Errorf("%s, response body content: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), string(bodyContent)) @@ -211,7 +211,49 @@ func (client *SyncClient) DownloadFromURL(downloadURL, filePath, uuid, diskUUID, bodyContent, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("%s, failed to read the response body: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), err) + return errors.Wrapf(err, "%v, failed to read the response body", util.GetHTTPClientErrorPrefix(resp.StatusCode)) + } + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("%s, response body content: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), string(bodyContent)) + } + + return nil +} + +func (client *SyncClient) CloneFromBackingImage(sourceBackingImage, sourceBackingImageUUID, encryption, filePath, uuid, diskUUID, expectedChecksum string, credential map[string]string) error { + httpClient := &http.Client{Timeout: 0} + encodedCredential, err := json.Marshal(credential) + if err != nil { + return err + } + + requestURL := fmt.Sprintf("http://%s/v1/files", client.Remote) + req, err := http.NewRequest("POST", requestURL, bytes.NewReader(encodedCredential)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + q := req.URL.Query() + q.Add("action", "cloneFromBackingImage") + q.Add("backing-image", sourceBackingImage) + q.Add("backing-image-uuid", sourceBackingImageUUID) + q.Add("encryption", encryption) + q.Add("file-path", filePath) + q.Add("uuid", uuid) + q.Add("disk-uuid", diskUUID) + q.Add("expected-checksum", expectedChecksum) + + req.URL.RawQuery = q.Encode() + + resp, err := httpClient.Do(req) + if err != nil { + return errors.Wrapf(err, "clone from backing image failed") + } + defer resp.Body.Close() + + bodyContent, err := io.ReadAll(resp.Body) + if err != nil { + return errors.Wrapf(err, "%v, failed to read the response body", util.GetHTTPClientErrorPrefix(resp.StatusCode)) } if resp.StatusCode != http.StatusOK { return fmt.Errorf("%s, response body content: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), string(bodyContent)) @@ -252,7 +294,7 @@ func (client *SyncClient) RestoreFromBackupURL(backupURL, concurrentLimit, fileP bodyContent, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("%s, failed to read the response body: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), err) + return errors.Wrapf(err, "%v, failed to read the response body", util.GetHTTPClientErrorPrefix(resp.StatusCode)) } if resp.StatusCode != http.StatusOK { return fmt.Errorf("%s, response body content: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), string(bodyContent)) @@ -312,7 +354,7 @@ func (client *SyncClient) Upload(src, dst, uuid, diskUUID, expectedChecksum stri bodyContent, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("%s, failed to read the response body: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), err) + return errors.Wrapf(err, "%v, failed to read the response body", util.GetHTTPClientErrorPrefix(resp.StatusCode)) } if resp.StatusCode != http.StatusOK { return fmt.Errorf("%s, response body content: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), string(bodyContent)) @@ -348,7 +390,7 @@ func (client *SyncClient) Receive(filePath, uuid, diskUUID, expectedChecksum, fi bodyContent, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("%s, failed to read the response body: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), err) + return errors.Wrapf(err, "%v, failed to read the response body", util.GetHTTPClientErrorPrefix(resp.StatusCode)) } if resp.StatusCode != http.StatusOK { return fmt.Errorf("%s, response body content: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), string(bodyContent)) @@ -378,7 +420,7 @@ func (client *SyncClient) Send(filePath, toAddress string) error { bodyContent, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("%s, failed to read the response body: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), err) + return errors.Wrapf(err, "%v, failed to read the response body", util.GetHTTPClientErrorPrefix(resp.StatusCode)) } if resp.StatusCode != http.StatusOK { return fmt.Errorf("%s, response body content: %v", util.GetHTTPClientErrorPrefix(resp.StatusCode), string(bodyContent)) diff --git a/pkg/crypto/crypto.go b/pkg/crypto/crypto.go new file mode 100644 index 00000000..f5104fd2 --- /dev/null +++ b/pkg/crypto/crypto.go @@ -0,0 +1,169 @@ +package crypto + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/longhorn/backing-image-manager/pkg/types" + lhns "github.com/longhorn/go-common-libs/ns" + lhtypes "github.com/longhorn/go-common-libs/types" +) + +const ( + MapperFilePathPrefix = "/dev/mapper" + + CryptoKeyDefaultCipher = "aes-xts-plain64" + CryptoKeyDefaultHash = "sha256" + CryptoKeyDefaultSize = "256" + CryptoDefaultPBKDF = "argon2i" +) + +// EncryptParams keeps the customized cipher options from the secret CR +type EncryptParams struct { + KeyProvider string + KeyCipher string + KeyHash string + KeySize string + PBKDF string +} + +func NewEncryptParams(keyProvider, keyCipher, keyHash, keySize, pbkdf string) *EncryptParams { + return &EncryptParams{KeyProvider: keyProvider, KeyCipher: keyCipher, KeyHash: keyHash, KeySize: keySize, PBKDF: pbkdf} +} + +func (cp *EncryptParams) GetKeyCipher() string { + if cp.KeyCipher == "" { + return CryptoKeyDefaultCipher + } + return cp.KeyCipher +} + +func (cp *EncryptParams) GetKeyHash() string { + if cp.KeyHash == "" { + return CryptoKeyDefaultHash + } + return cp.KeyHash +} + +func (cp *EncryptParams) GetKeySize() string { + if cp.KeySize == "" { + return CryptoKeyDefaultSize + } + return cp.KeySize +} + +func (cp *EncryptParams) GetPBKDF() string { + if cp.PBKDF == "" { + return CryptoDefaultPBKDF + } + return cp.PBKDF +} + +// EncryptBackingImage encrypts provided device with LUKS. +func EncryptBackingImage(devicePath, passphrase string, cryptoParams *EncryptParams) error { + namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc} + nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces) + if err != nil { + return err + } + + logrus.Infof("Encrypting device %s with LUKS", devicePath) + if _, err := nsexec.LuksFormat( + devicePath, passphrase, + cryptoParams.GetKeyCipher(), + cryptoParams.GetKeyHash(), + cryptoParams.GetKeySize(), + cryptoParams.GetPBKDF(), + lhtypes.LuksTimeout); err != nil { + return errors.Wrapf(err, "failed to encrypt device %s with LUKS", devicePath) + } + return nil +} + +// OpenBackingImage opens backing image so that it can be used by the client. +func OpenBackingImage(devicePath, passphrase, uuid string) error { + if isOpen, _ := IsEncryptedDeviceOpened(types.BackingImageMapper(uuid)); isOpen { + logrus.Infof("Device %s is already opened at %s", devicePath, types.BackingImageMapper(uuid)) + return nil + } + + namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc} + nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces) + if err != nil { + return err + } + + logrus.Infof("Opening device %s with LUKS on %v", devicePath, types.BackingImageFileName) + _, err = nsexec.LuksOpen(types.GetLuksBackingImageName(uuid), devicePath, passphrase, lhtypes.LuksTimeout) + if err != nil { + logrus.WithError(err).Warnf("Failed to open LUKS device %s", devicePath) + } + return err +} + +// CloseBackingImage closes encrypted backing image so it can be detached. +func CloseBackingImage(uuid string) error { + namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc} + nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces) + if err != nil { + return err + } + + logrus.Infof("Closing LUKS device %s", types.GetLuksBackingImageName(uuid)) + _, err = nsexec.LuksClose(types.GetLuksBackingImageName(uuid), lhtypes.LuksTimeout) + return err +} + +// IsEncryptedDeviceOpened determines if encrypted device is already open. +func IsEncryptedDeviceOpened(device string) (bool, error) { + _, mappedFile, err := DeviceEncryptionStatus(device) + return mappedFile != "", err +} + +// DeviceEncryptionStatus looks to identify if the passed device is a LUKS mapping +// and if so what the device is and the mapper name as used by LUKS. +// If not, just returns the original device and an empty string. +func DeviceEncryptionStatus(devicePath string) (mappedDevice, mapper string, err error) { + if !strings.HasPrefix(devicePath, types.MapperFilePathPrefix) { + return devicePath, "", nil + } + + namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc} + nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces) + if err != nil { + return devicePath, "", err + } + + backingImage := strings.TrimPrefix(devicePath, types.MapperFilePathPrefix+"/") + stdout, err := nsexec.LuksStatus(backingImage, lhtypes.LuksTimeout) + if err != nil { + logrus.WithError(err).Warnf("Device %s is not an active LUKS device", devicePath) + return devicePath, "", nil + } + + lines := strings.Split(string(stdout), "\n") + if len(lines) < 1 { + return "", "", fmt.Errorf("device encryption status returned no stdout for %s", devicePath) + } + + if !strings.Contains(lines[0], " is active") { + // Implies this is not a LUKS device + return devicePath, "", nil + } + + for i := 1; i < len(lines); i++ { + kv := strings.SplitN(strings.TrimSpace(lines[i]), ":", 2) + if len(kv) < 1 { + return "", "", fmt.Errorf("device encryption status output for %s is badly formatted: %s", + devicePath, lines[i]) + } + if strings.Compare(kv[0], "device") == 0 { + return strings.TrimSpace(kv[1]), backingImage, nil + } + } + // Identified as LUKS, but failed to identify a mapped device + return "", "", fmt.Errorf("mapped device not found in path %s", devicePath) +} diff --git a/pkg/datasource/service.go b/pkg/datasource/service.go index b3e29ac5..78185d25 100644 --- a/pkg/datasource/service.go +++ b/pkg/datasource/service.go @@ -140,14 +140,16 @@ func (s *Service) init() (err error) { }() switch s.sourceType { - case types.DataSourceTypeRestore: - return s.restoreFromBackupURL() case types.DataSourceTypeDownload: return s.downloadFromURL(s.parameters) case types.DataSourceTypeUpload: return s.prepareForUpload() case types.DataSourceTypeExportFromVolume: return s.exportFromVolume(s.parameters) + case types.DataSourceTypeRestore: + return s.restoreFromBackupURL() + case types.DataSourceTypeClone: + return s.cloneFromBackingImage() default: return fmt.Errorf("unknown data source type: %v", s.sourceType) } @@ -187,6 +189,34 @@ func (s *Service) waitForBeginning() { } } +func (s *Service) cloneFromBackingImage() (err error) { + sourceBackingImage := s.parameters[types.DataSourceTypeCloneParameterBackingImage] + if sourceBackingImage == "" { + return fmt.Errorf("%v is not specified", types.DataSourceTypeCloneParameterBackingImage) + } + + sourceBackingImageUUID := s.parameters[types.DataSourceTypeCloneParameterBackingImageUUID] + if sourceBackingImageUUID == "" { + return fmt.Errorf("%v is not specified", types.DataSourceTypeCloneParameterBackingImageUUID) + } + + encryption := s.parameters[types.DataSourceTypeCloneParameterEncryption] + if types.EncryptionType(encryption) != types.EncryptionTypeEncrypt && + types.EncryptionType(encryption) != types.EncryptionTypeDecrypt && + types.EncryptionType(encryption) != types.EncryptionTypeIgnore { + return fmt.Errorf("%v operation %v is not specified", types.DataSourceTypeCloneParameterEncryption, encryption) + } + + if types.EncryptionType(encryption) == types.EncryptionTypeEncrypt || + types.EncryptionType(encryption) == types.EncryptionTypeDecrypt { + if len(s.credential) == 0 { + return fmt.Errorf("secret is not provided for %v or %v", types.EncryptionTypeEncrypt, types.EncryptionTypeDecrypt) + } + } + + return s.syncClient.CloneFromBackingImage(sourceBackingImage, sourceBackingImageUUID, encryption, s.filePath, s.uuid, s.diskUUID, s.expectedChecksum, s.credential) +} + func (s *Service) restoreFromBackupURL() (err error) { backupURL := s.parameters[types.DataSourceTypeRestoreParameterBackupURL] if backupURL == "" { diff --git a/pkg/sync/handler.go b/pkg/sync/handler.go index ec774e6b..11e33d3e 100644 --- a/pkg/sync/handler.go +++ b/pkg/sync/handler.go @@ -91,7 +91,7 @@ func (h *HTTPHandler) DownloadFromURL(ctx context.Context, url, filePath string, } defer outFile.Close() - copied, err := IdleTimeoutCopy(ctx, cancel, resp.Body, outFile, updater) + copied, err := IdleTimeoutCopy(ctx, cancel, resp.Body, outFile, updater, false) if err != nil { return 0, err } @@ -104,7 +104,7 @@ func (h *HTTPHandler) DownloadFromURL(ctx context.Context, url, filePath string, } // IdleTimeoutCopy relies on ctx of the reader/src or a separate timer to interrupt the processing. -func IdleTimeoutCopy(ctx context.Context, cancel context.CancelFunc, src io.ReadCloser, dst io.WriteSeeker, updater ProgressUpdater) (copied int64, err error) { +func IdleTimeoutCopy(ctx context.Context, cancel context.CancelFunc, src io.ReadCloser, dst io.WriteSeeker, updater ProgressUpdater, writeZero bool) (copied int64, err error) { writeSeekCh := make(chan int64, 100) defer close(writeSeekCh) @@ -151,7 +151,7 @@ func IdleTimeoutCopy(ctx context.Context, cancel context.CancelFunc, src io.Read nr, rErr = src.Read(buf) if nr > 0 { // Skip writing zero data - if bytes.Equal(buf[0:nr], zeroByteArray[0:nr]) { + if !writeZero && bytes.Equal(buf[0:nr], zeroByteArray[0:nr]) { _, handleErr = dst.Seek(int64(nr), io.SeekCurrent) nws = int64(nr) } else { diff --git a/pkg/sync/router.go b/pkg/sync/router.go index af16ef56..3174fe5d 100644 --- a/pkg/sync/router.go +++ b/pkg/sync/router.go @@ -25,6 +25,7 @@ func NewRouter(service *Service) *mux.Router { router.HandleFunc("/v1/files", service.DownloadFromURL).Methods("POST").Queries("action", "downloadFromURL") router.HandleFunc("/v1/files", service.UploadFromRequest).Methods("POST").Queries("action", "upload") router.HandleFunc("/v1/files", service.ReceiveFromPeer).Methods("POST").Queries("action", "receiveFromPeer") + router.HandleFunc("/v1/files", service.CloneFromBackingImage).Methods("POST").Queries("action", "cloneFromBackingImage") router.HandleFunc("/debug/pprof/", pprof.Index).Methods("GET") router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline).Methods("GET") diff --git a/pkg/sync/service.go b/pkg/sync/service.go index 6e507570..0e10de9a 100644 --- a/pkg/sync/service.go +++ b/pkg/sync/service.go @@ -388,6 +388,73 @@ func (s *Service) doDownloadFromURL(request *http.Request) (err error) { return nil } +func (s *Service) CloneFromBackingImage(writer http.ResponseWriter, request *http.Request) { + err := s.doCloneFromBackingImage(request) + if err != nil { + http.Error(writer, err.Error(), http.StatusInternalServerError) + return + } +} + +func (s *Service) doCloneFromBackingImage(request *http.Request) (err error) { + defer func() { + if err != nil { + s.log.WithError(err).Errorf("Sync Service: failed to do clone from backing image") + } + }() + + queryParams := request.URL.Query() + sourceBackingImage := queryParams.Get(types.DataSourceTypeCloneParameterBackingImage) + if sourceBackingImage == "" { + return fmt.Errorf("%v is not specified", types.DataSourceTypeCloneParameterBackingImage) + } + + sourceBackingImageUUID := queryParams.Get(types.DataSourceTypeCloneParameterBackingImageUUID) + if sourceBackingImageUUID == "" { + return fmt.Errorf("%v is not specified", types.DataSourceTypeCloneParameterBackingImageUUID) + } + + encryption := types.EncryptionType(queryParams.Get(types.DataSourceTypeCloneParameterEncryption)) + if encryption != types.EncryptionTypeEncrypt && encryption != types.EncryptionTypeDecrypt && encryption != types.EncryptionTypeIgnore { + return fmt.Errorf("%v operation %v is not specified", types.DataSourceTypeCloneParameterEncryption, encryption) + } + filePath := queryParams.Get("file-path") + if filePath == "" { + return fmt.Errorf("no filePath restoring file") + } + uuid := queryParams.Get("uuid") + if uuid == "" { + return fmt.Errorf("no uuid for restoring file") + } + diskUUID := queryParams.Get("disk-uuid") + expectedChecksum := queryParams.Get("expected-checksum") + + credential := map[string]string{} + if err := json.NewDecoder(request.Body).Decode(&credential); err != nil { + if err != io.EOF { + return errors.Wrap(err, "failed to get credential failed from request") + } + } + + sf, err := s.checkAndInitSyncFile(filePath, uuid, diskUUID, expectedChecksum, 0) + if err != nil { + return err + } + go func() { + if err := sf.WaitForStateNonPending(); err != nil { + s.log.Errorf("Sync Service: failed to wait for sync file %v becoming non-pending state before starting the actual cloning: %v", filePath, err) + return + } + + if _, err := sf.CloneToFileWithEncryption(sourceBackingImage, sourceBackingImageUUID, encryption, credential); err != nil { + s.log.Errorf("Sync Service: failed to clone sync file %v: %v", filePath, err) + return + } + }() + + return nil +} + func (s *Service) RestoreFromBackupURL(writer http.ResponseWriter, request *http.Request) { err := s.doRestoreFromBackupURL(request) if err != nil { diff --git a/pkg/sync/sync_file.go b/pkg/sync/sync_file.go index 63b93677..a43679c2 100644 --- a/pkg/sync/sync_file.go +++ b/pkg/sync/sync_file.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "strconv" + "strings" "sync" "time" @@ -15,10 +16,13 @@ import ( "github.com/sirupsen/logrus" butil "github.com/longhorn/backupstore/util" + lhns "github.com/longhorn/go-common-libs/ns" + lhtypes "github.com/longhorn/go-common-libs/types" sparserest "github.com/longhorn/sparse-tools/sparse/rest" "github.com/longhorn/backing-image-manager/api" "github.com/longhorn/backing-image-manager/pkg/backup" + "github.com/longhorn/backing-image-manager/pkg/crypto" "github.com/longhorn/backing-image-manager/pkg/types" "github.com/longhorn/backing-image-manager/pkg/util" ) @@ -398,7 +402,7 @@ func (sf *SyncingFile) GetFileReader() (io.ReadCloser, error) { return os.Open(sf.filePath) } -func (sf *SyncingFile) stateCheckBeforeProcessing() (bool, error) { +func (sf *SyncingFile) isProcessingRequired() (bool, error) { sf.lock.RLock() defer sf.lock.RUnlock() @@ -432,7 +436,7 @@ func (sf *SyncingFile) Fetch(srcFilePath string) (err error) { } }() - needProcessing, err := sf.stateCheckBeforeProcessing() + needProcessing, err := sf.isProcessingRequired() if err != nil { return err } @@ -480,7 +484,7 @@ func (sf *SyncingFile) Fetch(srcFilePath string) (err error) { func (sf *SyncingFile) DownloadFromURL(url string) (written int64, err error) { sf.log.Infof("SyncingFile: start to download sync file from URL %v", url) - needProcessing, err := sf.stateCheckBeforeProcessing() + needProcessing, err := sf.isProcessingRequired() if err != nil { return 0, err } @@ -509,7 +513,7 @@ func (sf *SyncingFile) DownloadFromURL(url string) (written int64, err error) { func (sf *SyncingFile) RestoreFromBackupURL(backupURL string, credential map[string]string, concurrentLimit int) (err error) { sf.log.Infof("SyncingFile: start to restore sync file from backup URL %v", backupURL) - needProcessing, err := sf.stateCheckBeforeProcessing() + needProcessing, err := sf.isProcessingRequired() if err != nil { return err } @@ -577,6 +581,177 @@ func (sf *SyncingFile) waitForRestoreComplete() (err error) { return nil } +// CloneToFileWithEncryption clone the backing file on the same node to another backing file with the given encryption operation. +// when doing encryption, it creates a loop device from the target backing file, setup the encrypted device from the loop device and then dump the data from the source file to the target encrypted device. +// When doing decryption, it creates a loop device from the source backing file, setup the encrypted device from the loop device and then dump the data from the source encrypted device to the target file. +// When doing ignore clone, it directly dumps the data from the source backing file to the target backing file. +func (sf *SyncingFile) CloneToFileWithEncryption(sourceBackingImage, sourceBackingImageUUID string, encryption types.EncryptionType, credential map[string]string) (copied int64, err error) { + sf.log.Infof("SyncingFile: start to clone the file") + + defer func() { + if err != nil { + sf.log.Errorf("SyncingFile: failed CloneToFileWithEncryption: %v", err) + } + }() + + needProcessing, err := sf.isProcessingRequired() + if err != nil { + return 0, err + } + if !needProcessing { + return 0, nil + } + defer func() { + if finalErr := sf.finishProcessing(err); finalErr != nil { + err = finalErr + } + }() + + sourceFile, tmpRawFile, writeZero, err := sf.prepareCloneSourceFile(sourceBackingImage, sourceBackingImageUUID, encryption) + defer func() { + if tmpRawFile != "" { + os.RemoveAll(tmpRawFile) + } + }() + if err != nil { + return 0, errors.Wrapf(err, "failed to prepare source file") + } + + err = sf.prepareCloneTargetFile(sourceFile, encryption) + if err != nil { + return 0, errors.Wrapf(err, "failed to prepare target file") + } + + sourceFileReader, sourceLoopDevicePath, err := sf.openCloneSourceFile(sourceFile, encryption, credential) + defer func() { + if sourceFileReader != nil { + sourceFileReader.Close() + } + if sourceLoopDevicePath == "" { + sf.closeCryptoDevice(sourceLoopDevicePath) + } + }() + if err != nil { + return 0, errors.Wrapf(err, "failed to open clone source file") + } + + targetFileWriter, targetLoopDevicePath, err := sf.openCloneTargetFile(encryption, credential) + defer func() { + if targetFileWriter != nil { + targetFileWriter.Close() + } + if targetLoopDevicePath == "" { + sf.closeCryptoDevice(targetLoopDevicePath) + } + }() + if err != nil { + return 0, errors.Wrapf(err, "failed to open clone source file") + } + + nw, err := IdleTimeoutCopy(sf.ctx, sf.cancel, sourceFileReader, targetFileWriter, sf, writeZero) + if err != nil { + err = errors.Wrapf(err, "failed to copy the data with timeout") + } + return nw, err +} + +func (sf *SyncingFile) openCloneSourceFile(sourceFile string, encryption types.EncryptionType, credential map[string]string) (*os.File, string, error) { + loopDevicePath := "" + if encryption == types.EncryptionTypeDecrypt { + loopDevicePath, err := sf.setupCryptoDevice(sourceFile, false, credential) + if err != nil { + return nil, loopDevicePath, errors.Wrapf(err, "failed to setup the crypto device with the file %v during cloning", sourceFile) + } + sourceFile = types.BackingImageMapper(sf.uuid) + } + + if _, err := os.Stat(sourceFile); err != nil { + return nil, loopDevicePath, errors.Wrapf(err, "%v not found during cloning", sourceFile) + } + + sourceFileReader, err := os.Open(sourceFile) + if err != nil { + return nil, loopDevicePath, errors.Wrapf(err, "Error opening file %v", sourceFile) + } + + return sourceFileReader, loopDevicePath, nil +} + +func (sf *SyncingFile) openCloneTargetFile(encryption types.EncryptionType, credential map[string]string) (*os.File, string, error) { + loopDevicePath := "" + targetFile := sf.tmpFilePath + + if encryption == types.EncryptionTypeEncrypt { + loopDevicePath, err := sf.setupCryptoDevice(targetFile, true, credential) + if err != nil { + return nil, loopDevicePath, errors.Wrapf(err, "failed to setup the crypto device with the file %v during cloning", targetFile) + } + targetFile = types.BackingImageMapper(sf.uuid) + } + + if _, err := os.Stat(targetFile); err != nil { + return nil, loopDevicePath, errors.Wrapf(err, "%v not found during cloning", targetFile) + } + + targetFileWriter, err := os.OpenFile(targetFile, os.O_RDWR, 0666) + if err != nil { + return nil, loopDevicePath, errors.Wrapf(err, "Error opening file %v", targetFile) + } + return targetFileWriter, loopDevicePath, nil +} + +func (sf *SyncingFile) prepareCloneSourceFile(sourceBackingImage, sourceBackingImageUUID string, encryption types.EncryptionType) (string, string, bool, error) { + writeZero := false + sourceFile := types.GetBackingImageFilePath(types.DiskPathInContainer, sourceBackingImage, sourceBackingImageUUID) + tmpRawFile := "" + + if _, err := os.Stat(sourceFile); err != nil { + return "", tmpRawFile, writeZero, errors.Wrapf(err, "source file %v not found ", sourceFile) + } + + if encryption == types.EncryptionTypeEncrypt { + // we should write zero when doing encryption so the zero can be encrypted as well + writeZero = true + + // If the source file is qcow2 when encrypting we need to convert and use its raw image + imgInfo, err := util.GetQemuImgInfo(sourceFile) + if err != nil { + return "", tmpRawFile, writeZero, errors.Wrapf(err, "failed to get source backing file %v qemu info", sourceFile) + } + if imgInfo.Format == "qcow2" { + tmpRawFile := fmt.Sprintf("%v-raw.tmp", sourceFile) + + if err := util.ConvertFromQcow2ToRaw(sourceFile, tmpRawFile); err != nil { + return "", tmpRawFile, writeZero, errors.Wrapf(err, "failed to create raw image from qcow2 image %v", sourceFile) + } + // use the raw image as source when doing encryption + sourceFile = tmpRawFile + } + } + + return sourceFile, tmpRawFile, writeZero, nil +} + +func (sf *SyncingFile) prepareCloneTargetFile(sourceFile string, encryption types.EncryptionType) error { + info, err := os.Stat(sourceFile) + if err != nil { + return err + } + sourceFileSize := info.Size() + if err := sf.setFileSizeForEncryption(sourceFileSize, encryption); err != nil { + return errors.Wrap(err, "failed to set size for the target file") + } + f, err := os.OpenFile(sf.tmpFilePath, os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + return err + } + if err = f.Truncate(sf.size); err != nil { + return err + } + f.Close() + return nil +} + func (sf *SyncingFile) IdleTimeoutCopyToFile(src io.ReadCloser) (copied int64, err error) { sf.log.Infof("SyncingFile: start to copy data to sync file") @@ -586,7 +761,7 @@ func (sf *SyncingFile) IdleTimeoutCopyToFile(src io.ReadCloser) (copied int64, e } }() - needProcessing, err := sf.stateCheckBeforeProcessing() + needProcessing, err := sf.isProcessingRequired() if err != nil { return 0, err } @@ -609,7 +784,7 @@ func (sf *SyncingFile) IdleTimeoutCopyToFile(src io.ReadCloser) (copied int64, e } }() - nw, err := IdleTimeoutCopy(sf.ctx, sf.cancel, src, f, sf) + nw, err := IdleTimeoutCopy(sf.ctx, sf.cancel, src, f, sf, false) if err != nil { err = errors.Wrapf(err, "failed to copy the data with timeout") } @@ -619,7 +794,7 @@ func (sf *SyncingFile) IdleTimeoutCopyToFile(src io.ReadCloser) (copied int64, e func (sf *SyncingFile) Receive(port int, fileType string) (err error) { sf.log.Infof("SyncingFile: start to launch a receiver at port %v", port) - needProcessing, err := sf.stateCheckBeforeProcessing() + needProcessing, err := sf.isProcessingRequired() if err != nil { return err } @@ -861,3 +1036,86 @@ func (sf *SyncingFile) writeConfigNoLock() { sf.log.Warnf("SyncingFile: failed to write config file when the file becomes ready: %v", err) } } + +func (sf *SyncingFile) setFileSizeForEncryption(sourceSize int64, encryption types.EncryptionType) error { + sf.lock.Lock() + if encryption == types.EncryptionTypeIgnore { + sf.size = sourceSize + } else if encryption == types.EncryptionTypeEncrypt { + // LUKS 16MB data size is introduced here: https://gitlab.com/cryptsetup/cryptsetup/-/blob/master/docs/v2.1.0-ReleaseNotes#L27 + sf.size = sourceSize + types.EncryptionMetaSize + // The meta data size won't be counted when copying the data + // need to init the processedSize with the meta data size + sf.processedSize = types.EncryptionMetaSize + } else if encryption == types.EncryptionTypeDecrypt { + sf.size = sourceSize - types.EncryptionMetaSize + } + sf.lock.Unlock() + + if sf.size < 0 { + return fmt.Errorf("file size is smaller than 0 while encryption or decryption") + } + return nil +} + +func (sf *SyncingFile) setupCryptoDevice(file string, needFormat bool, credential map[string]string) (string, error) { + namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceNet} + nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.ProcDirectory, namespaces) + if err != nil { + return "", err + } + + output, err := nsexec.Execute(nil, "losetup", []string{"-f"}, types.CommandExecutionTimeout) + if err != nil { + return "", err + } + loopDevicePath := strings.TrimSpace(output) + if loopDevicePath == "" { + return "", fmt.Errorf("failed to get valid loop device path") + } + + if _, err := nsexec.Execute(nil, "losetup", []string{loopDevicePath, file}, types.CommandExecutionTimeout); err != nil { + return loopDevicePath, err + } + + keyProvider := credential[lhtypes.CryptoKeyProvider] + passphrase := credential[lhtypes.CryptoKeyValue] + if keyProvider != "" && keyProvider != "secret" { + return loopDevicePath, fmt.Errorf("unsupported key provider %v for encryption", keyProvider) + } + if len(passphrase) == 0 { + return loopDevicePath, fmt.Errorf("missing passphrase for encryption") + } + + // If we are working on encryption, the target device has not been formatted with the cryptsetup and it needs to be formatted first. + // If we are working on decryption, the source is a encrypted file and we don't need to format the source device again or the metadata will be replaced. + if needFormat { + cryptoParams := crypto.NewEncryptParams(keyProvider, credential[lhtypes.CryptoKeyCipher], credential[lhtypes.CryptoKeyHash], credential[lhtypes.CryptoKeySize], credential[lhtypes.CryptoPBKDF]) + if err := crypto.EncryptBackingImage(loopDevicePath, passphrase, cryptoParams); err != nil { + return loopDevicePath, err + } + } + + if err := crypto.OpenBackingImage(loopDevicePath, passphrase, sf.uuid); err != nil { + return loopDevicePath, err + } + + return loopDevicePath, nil +} + +func (sf *SyncingFile) closeCryptoDevice(loopDevicePath string) { + if err := crypto.CloseBackingImage(sf.uuid); err != nil { + sf.log.WithError(err).Warnf("failed to close the crypto device of backing file") + } + + namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceNet} + nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.ProcDirectory, namespaces) + if err != nil { + sf.log.WithError(err).Warnf("failed to setup nsexec to detach the loop device after cloning") + } + + output, err := nsexec.Execute(nil, "losetup", []string{"-d", loopDevicePath}, types.CommandExecutionTimeout) + if err != nil { + sf.log.WithError(err).Warnf("failed to detach the loop device after cloning, output: %v", output) + } +} diff --git a/pkg/types/types.go b/pkg/types/types.go index b43a5543..1d17abd8 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -2,6 +2,7 @@ package types import ( "fmt" + "path" "path/filepath" "strings" "time" @@ -19,9 +20,10 @@ const ( DefaultSyncServerPort = 8001 DefaultVolumeExportReceiverPort = 8002 - GRPCServiceTimeout = 1 * time.Minute - HTTPTimeout = 4 * time.Second - MonitorInterval = 3 * time.Second + GRPCServiceTimeout = 1 * time.Minute + HTTPTimeout = 4 * time.Second + MonitorInterval = 3 * time.Second + CommandExecutionTimeout = 10 * time.Second FileSyncHTTPClientTimeout = 5 // TODO: use 5 seconds as default, need to refactor it @@ -30,6 +32,9 @@ const ( BackingImageFileName = "backing" TmpFileSuffix = ".tmp" BackingImageTmpFileName = BackingImageFileName + TmpFileSuffix + + MapperFilePathPrefix = "/dev/mapper" + EncryptionMetaSize = 16 * 1024 * 1024 // 16MB ) type State string @@ -52,9 +57,14 @@ const ( DataSourceTypeUpload = DataSourceType("upload") DataSourceTypeExportFromVolume = DataSourceType("export-from-volume") DataSourceTypeRestore = DataSourceType("restore") + DataSourceTypeClone = DataSourceType("clone") ) const ( + DataSourceTypeCloneParameterBackingImage = "backing-image" + DataSourceTypeCloneParameterBackingImageUUID = "backing-image-uuid" + DataSourceTypeCloneParameterEncryption = "encryption" + DataSourceTypeDownloadParameterURL = "url" DataSourceTypeRestoreParameterBackupURL = "backup-url" DataSourceTypeRestoreParameterConcurrentLimit = "concurrent-limit" @@ -73,6 +83,14 @@ const ( SyncingFileTypeQcow2 = "qcow2" ) +type EncryptionType string + +const ( + EncryptionTypeEncrypt = EncryptionType("encrypt") + EncryptionTypeDecrypt = EncryptionType("decrypt") + EncryptionTypeIgnore = EncryptionType("ignore") +) + func GetDataSourceFileName(biName, biUUID string) string { return fmt.Sprintf("%s-%s", biName, biUUID) } @@ -97,3 +115,11 @@ func GetBackingImageNameFromFilePath(biFilePath, biUUID string) string { biDirName := filepath.Join(filepath.Base(filepath.Dir(biFilePath))) return strings.TrimSuffix(biDirName, "-"+biUUID) } + +func BackingImageMapper(uuid string) string { + return path.Join(MapperFilePathPrefix, GetLuksBackingImageName(uuid)) +} + +func GetLuksBackingImageName(uuid string) string { + return fmt.Sprintf("%v-%v", BackingImageFileName, uuid) +} diff --git a/pkg/util/util.go b/pkg/util/util.go index c2e8a538..2bcd23cb 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -300,6 +300,19 @@ func ConvertFromRawToQcow2(filePath string) error { return os.Rename(tmpFilePath, filePath) } +func ConvertFromQcow2ToRaw(sourcePath, targetPath string) error { + if imgInfo, err := GetQemuImgInfo(sourcePath); err != nil { + return err + } else if imgInfo.Format == "raw" { + return nil + } + + if _, err := Execute([]string{}, QemuImgBinary, "convert", "-f", "qcow2", "-O", "raw", sourcePath, targetPath); err != nil { + return err + } + return nil +} + func FileModificationTime(filePath string) string { fi, err := os.Stat(filePath) if err != nil { diff --git a/vendor/github.com/c9s/goprocinfo/LICENSE b/vendor/github.com/c9s/goprocinfo/LICENSE new file mode 100644 index 00000000..ea31a4ab --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2014 Yo-An Lin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/c9s/goprocinfo/linux/cpuinfo.go b/vendor/github.com/c9s/goprocinfo/linux/cpuinfo.go new file mode 100644 index 00000000..4564659f --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/cpuinfo.go @@ -0,0 +1,133 @@ +package linux + +import ( + "io/ioutil" + "regexp" + "strconv" + "strings" +) + +type CPUInfo struct { + Processors []Processor `json:"processors"` +} + +func (self *CPUInfo) NumCPU() int { + return len(self.Processors) +} + +func (self *CPUInfo) NumCore() int { + core := make(map[string]bool) + + for _, p := range self.Processors { + pid := p.PhysicalId + cid := p.CoreId + + if pid == -1 { + return self.NumCPU() + } else { + // to avoid fmt import + key := strconv.FormatInt(int64(pid), 10) + ":" + strconv.FormatInt(int64(cid), 10) + core[key] = true + } + } + + return len(core) +} + +func (self *CPUInfo) NumPhysicalCPU() int { + pcpu := make(map[string]bool) + + for _, p := range self.Processors { + pid := p.PhysicalId + + if pid == -1 { + return self.NumCPU() + } else { + // to avoid fmt import + key := strconv.FormatInt(int64(pid), 10) + pcpu[key] = true + } + } + + return len(pcpu) +} + +type Processor struct { + Id int64 `json:"id"` + VendorId string `json:"vendor_id"` + Model int64 `json:"model"` + ModelName string `json:"model_name"` + Flags []string `json:"flags"` + Cores int64 `json:"cores"` + MHz float64 `json:"mhz"` + CacheSize int64 `json:"cache_size"` // KB + PhysicalId int64 `json:"physical_id"` + CoreId int64 `json:"core_id"` +} + +var cpuinfoRegExp = regexp.MustCompile("([^:]*?)\\s*:\\s*(.*)$") + +func ReadCPUInfo(path string) (*CPUInfo, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + content := string(b) + lines := strings.Split(content, "\n") + + var cpuinfo = CPUInfo{} + var processor = &Processor{CoreId: -1, PhysicalId: -1} + + for i, line := range lines { + var key string + var value string + + if len(line) == 0 && i != len(lines)-1 { + // end of processor + cpuinfo.Processors = append(cpuinfo.Processors, *processor) + processor = &Processor{} + continue + } else if i == len(lines)-1 { + continue + } + + submatches := cpuinfoRegExp.FindStringSubmatch(line) + key = submatches[1] + value = submatches[2] + + switch key { + case "processor": + processor.Id, _ = strconv.ParseInt(value, 10, 64) + case "vendor_id": + processor.VendorId = value + case "model": + processor.Model, _ = strconv.ParseInt(value, 10, 64) + case "model name": + processor.ModelName = value + case "flags": + processor.Flags = strings.Fields(value) + case "cpu cores": + processor.Cores, _ = strconv.ParseInt(value, 10, 64) + case "cpu MHz": + processor.MHz, _ = strconv.ParseFloat(value, 64) + case "cache size": + processor.CacheSize, _ = strconv.ParseInt(value[:strings.IndexAny(value, " \t\n")], 10, 64) + if strings.HasSuffix(line, "MB") { + processor.CacheSize *= 1024 + } + case "physical id": + processor.PhysicalId, _ = strconv.ParseInt(value, 10, 64) + case "core id": + processor.CoreId, _ = strconv.ParseInt(value, 10, 64) + } + /* + processor : 0 + vendor_id : GenuineIntel + cpu family : 6 + model : 26 + model name : Intel(R) Xeon(R) CPU L5520 @ 2.27GHz + */ + } + return &cpuinfo, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/disk.go b/vendor/github.com/c9s/goprocinfo/linux/disk.go new file mode 100644 index 00000000..91d7351b --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/disk.go @@ -0,0 +1,26 @@ +package linux + +import ( + "syscall" +) + +type Disk struct { + All uint64 `json:"all"` + Used uint64 `json:"used"` + Free uint64 `json:"free"` + FreeInodes uint64 `json:"freeInodes"` +} + +func ReadDisk(path string) (*Disk, error) { + fs := syscall.Statfs_t{} + err := syscall.Statfs(path, &fs) + if err != nil { + return nil, err + } + disk := Disk{} + disk.All = fs.Blocks * uint64(fs.Bsize) + disk.Free = fs.Bfree * uint64(fs.Bsize) + disk.Used = disk.All - disk.Free + disk.FreeInodes = fs.Ffree + return &disk, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/diskstat.go b/vendor/github.com/c9s/goprocinfo/linux/diskstat.go new file mode 100644 index 00000000..7c57332e --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/diskstat.go @@ -0,0 +1,100 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" + "time" +) + +// DiskStat is disk statistics to help measure disk activity. +// +// Note: +// * On a very busy or long-lived system values may wrap. +// * No kernel locks are held while modifying these counters. This implies that +// minor inaccuracies may occur. +// +// More more info see: +// https://www.kernel.org/doc/Documentation/iostats.txt and +// https://www.kernel.org/doc/Documentation/block/stat.txt +type DiskStat struct { + Major int `json:"major"` // major device number + Minor int `json:"minor"` // minor device number + Name string `json:"name"` // device name + ReadIOs uint64 `json:"read_ios"` // number of read I/Os processed + ReadMerges uint64 `json:"read_merges"` // number of read I/Os merged with in-queue I/O + ReadSectors uint64 `json:"read_sectors"` // number of 512 byte sectors read + ReadTicks uint64 `json:"read_ticks"` // total wait time for read requests in milliseconds + WriteIOs uint64 `json:"write_ios"` // number of write I/Os processed + WriteMerges uint64 `json:"write_merges"` // number of write I/Os merged with in-queue I/O + WriteSectors uint64 `json:"write_sectors"` // number of 512 byte sectors written + WriteTicks uint64 `json:"write_ticks"` // total wait time for write requests in milliseconds + InFlight uint64 `json:"in_flight"` // number of I/Os currently in flight + IOTicks uint64 `json:"io_ticks"` // total time this block device has been active in milliseconds + TimeInQueue uint64 `json:"time_in_queue"` // total wait time for all requests in milliseconds +} + +// ReadDiskStats reads and parses the file. +// +// Note: +// * Assumes a well formed file and will panic if it isn't. +func ReadDiskStats(path string) ([]DiskStat, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + devices := strings.Split(string(data), "\n") + results := make([]DiskStat, len(devices)-1) + + for i := range results { + fields := strings.Fields(devices[i]) + Major, _ := strconv.ParseInt(fields[0], 10, strconv.IntSize) + results[i].Major = int(Major) + Minor, _ := strconv.ParseInt(fields[1], 10, strconv.IntSize) + results[i].Minor = int(Minor) + results[i].Name = fields[2] + results[i].ReadIOs, _ = strconv.ParseUint(fields[3], 10, 64) + results[i].ReadMerges, _ = strconv.ParseUint(fields[4], 10, 64) + results[i].ReadSectors, _ = strconv.ParseUint(fields[5], 10, 64) + results[i].ReadTicks, _ = strconv.ParseUint(fields[6], 10, 64) + results[i].WriteIOs, _ = strconv.ParseUint(fields[7], 10, 64) + results[i].WriteMerges, _ = strconv.ParseUint(fields[8], 10, 64) + results[i].WriteSectors, _ = strconv.ParseUint(fields[9], 10, 64) + results[i].WriteTicks, _ = strconv.ParseUint(fields[10], 10, 64) + results[i].InFlight, _ = strconv.ParseUint(fields[11], 10, 64) + results[i].IOTicks, _ = strconv.ParseUint(fields[12], 10, 64) + results[i].TimeInQueue, _ = strconv.ParseUint(fields[13], 10, 64) + } + + return results, nil +} + +// GetReadBytes returns the number of bytes read. +func (ds *DiskStat) GetReadBytes() int64 { + return int64(ds.ReadSectors) * 512 +} + +// GetReadTicks returns the duration waited for read requests. +func (ds *DiskStat) GetReadTicks() time.Duration { + return time.Duration(ds.ReadTicks) * time.Millisecond +} + +// GetWriteBytes returns the number of bytes written. +func (ds *DiskStat) GetWriteBytes() int64 { + return int64(ds.WriteSectors) * 512 +} + +// GetWriteTicks returns the duration waited for write requests. +func (ds *DiskStat) GetWriteTicks() time.Duration { + return time.Duration(ds.WriteTicks) * time.Millisecond +} + +// GetIOTicks returns the duration the disk has been active. +func (ds *DiskStat) GetIOTicks() time.Duration { + return time.Duration(ds.IOTicks) * time.Millisecond +} + +// GetTimeInQueue returns the duration waited for all requests. +func (ds *DiskStat) GetTimeInQueue() time.Duration { + return time.Duration(ds.TimeInQueue) * time.Millisecond +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/interrupts.go b/vendor/github.com/c9s/goprocinfo/linux/interrupts.go new file mode 100644 index 00000000..21699be5 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/interrupts.go @@ -0,0 +1,56 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +type Interrupt struct { + Name string + Counts []uint64 + Description string +} + +type Interrupts struct { + Interrupts []Interrupt +} + +func ReadInterrupts(path string) (*Interrupts, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + content := string(b) + lines := strings.Split(content, "\n") + cpus := lines[0] + lines = append(lines[:0], lines[1:]...) + numCpus := len(strings.Fields(cpus)) + interrupts := make([]Interrupt, 0) + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) == 0 { + continue + } + counts := make([]uint64, 0) + i := 0 + for ; i < numCpus; i++ { + if len(fields) <= i+1 { + break + } + count, err := strconv.ParseInt(fields[i+1], 10, 64) + if err != nil { + return nil, err + } + counts = append(counts, uint64(count)) + } + name := strings.TrimSuffix(fields[0], ":") + description := strings.Join(fields[i+1:], " ") + interrupts = append(interrupts, Interrupt{ + Name: name, + Counts: counts, + Description: description, + }) + } + return &Interrupts{Interrupts: interrupts}, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/loadavg.go b/vendor/github.com/c9s/goprocinfo/linux/loadavg.go new file mode 100644 index 00000000..ad75fa94 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/loadavg.go @@ -0,0 +1,67 @@ +package linux + +import ( + "errors" + "io/ioutil" + "strconv" + "strings" +) + +type LoadAvg struct { + Last1Min float64 `json:"last1min"` + Last5Min float64 `json:"last5min"` + Last15Min float64 `json:"last15min"` + ProcessRunning uint64 `json:"process_running"` + ProcessTotal uint64 `json:"process_total"` + LastPID uint64 `json:"last_pid"` +} + +func ReadLoadAvg(path string) (*LoadAvg, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + content := strings.TrimSpace(string(b)) + fields := strings.Fields(content) + + if len(fields) < 5 { + return nil, errors.New("Cannot parse loadavg: " + content) + } + + process := strings.Split(fields[3], "/") + + if len(process) != 2 { + return nil, errors.New("Cannot parse loadavg: " + content) + } + + loadavg := LoadAvg{} + + if loadavg.Last1Min, err = strconv.ParseFloat(fields[0], 64); err != nil { + return nil, err + } + + if loadavg.Last5Min, err = strconv.ParseFloat(fields[1], 64); err != nil { + return nil, err + } + + if loadavg.Last15Min, err = strconv.ParseFloat(fields[2], 64); err != nil { + return nil, err + } + + if loadavg.ProcessRunning, err = strconv.ParseUint(process[0], 10, 64); err != nil { + return nil, err + } + + if loadavg.ProcessTotal, err = strconv.ParseUint(process[1], 10, 64); err != nil { + return nil, err + } + + if loadavg.LastPID, err = strconv.ParseUint(fields[4], 10, 64); err != nil { + return nil, err + } + + return &loadavg, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/meminfo.go b/vendor/github.com/c9s/goprocinfo/linux/meminfo.go new file mode 100644 index 00000000..09d76281 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/meminfo.go @@ -0,0 +1,97 @@ +package linux + +import ( + "io/ioutil" + "reflect" + "strconv" + "strings" +) + +type MemInfo struct { + MemTotal uint64 `json:"mem_total"` + MemFree uint64 `json:"mem_free"` + MemAvailable uint64 `json:"mem_available"` + Buffers uint64 `json:"buffers"` + Cached uint64 `json:"cached"` + SwapCached uint64 `json:"swap_cached"` + Active uint64 `json:"active"` + Inactive uint64 `json:"inactive"` + ActiveAnon uint64 `json:"active_anon" field:"Active(anon)"` + InactiveAnon uint64 `json:"inactive_anon" field:"Inactive(anon)"` + ActiveFile uint64 `json:"active_file" field:"Active(file)"` + InactiveFile uint64 `json:"inactive_file" field:"Inactive(file)"` + Unevictable uint64 `json:"unevictable"` + Mlocked uint64 `json:"mlocked"` + SwapTotal uint64 `json:"swap_total"` + SwapFree uint64 `json:"swap_free"` + Dirty uint64 `json:"dirty"` + Writeback uint64 `json:"write_back"` + AnonPages uint64 `json:"anon_pages"` + Mapped uint64 `json:"mapped"` + Shmem uint64 `json:"shmem"` + Slab uint64 `json:"slab"` + SReclaimable uint64 `json:"s_reclaimable"` + SUnreclaim uint64 `json:"s_unclaim"` + KernelStack uint64 `json:"kernel_stack"` + PageTables uint64 `json:"page_tables"` + NFS_Unstable uint64 `json:"nfs_unstable"` + Bounce uint64 `json:"bounce"` + WritebackTmp uint64 `json:"writeback_tmp"` + CommitLimit uint64 `json:"commit_limit"` + Committed_AS uint64 `json:"committed_as"` + VmallocTotal uint64 `json:"vmalloc_total"` + VmallocUsed uint64 `json:"vmalloc_used"` + VmallocChunk uint64 `json:"vmalloc_chunk"` + HardwareCorrupted uint64 `json:"hardware_corrupted"` + AnonHugePages uint64 `json:"anon_huge_pages"` + HugePages_Total uint64 `json:"huge_pages_total"` + HugePages_Free uint64 `json:"huge_pages_free"` + HugePages_Rsvd uint64 `json:"huge_pages_rsvd"` + HugePages_Surp uint64 `json:"huge_pages_surp"` + Hugepagesize uint64 `json:"hugepagesize"` + DirectMap4k uint64 `json:"direct_map_4k"` + DirectMap2M uint64 `json:"direct_map_2M"` + DirectMap1G uint64 `json:"direct_map_1G"` +} + +func ReadMemInfo(path string) (*MemInfo, error) { + data, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(data), "\n") + + // Maps a meminfo metric to its value (i.e. MemTotal --> 100000) + statMap := make(map[string]uint64) + + var info = MemInfo{} + + for _, line := range lines { + fields := strings.SplitN(line, ":", 2) + if len(fields) < 2 { + continue + } + valFields := strings.Fields(fields[1]) + val, _ := strconv.ParseUint(valFields[0], 10, 64) + statMap[fields[0]] = val + } + + elem := reflect.ValueOf(&info).Elem() + typeOfElem := elem.Type() + + for i := 0; i < elem.NumField(); i++ { + val, ok := statMap[typeOfElem.Field(i).Name] + if ok { + elem.Field(i).SetUint(val) + continue + } + val, ok = statMap[typeOfElem.Field(i).Tag.Get("field")] + if ok { + elem.Field(i).SetUint(val) + } + } + + return &info, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/mounts.go b/vendor/github.com/c9s/goprocinfo/linux/mounts.go new file mode 100644 index 00000000..5aa4925f --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/mounts.go @@ -0,0 +1,49 @@ +package linux + +import ( + "bufio" + "os" + "strings" +) + +type Mounts struct { + Mounts []Mount `json:"mounts"` +} + +type Mount struct { + Device string `json:"device"` + MountPoint string `json:"mountpoint"` + FSType string `json:"fstype"` + Options string `json:"options"` +} + +const ( + DefaultBufferSize = 1024 +) + +func ReadMounts(path string) (*Mounts, error) { + fin, err := os.Open(path) + if err != nil { + return nil, err + } + defer fin.Close() + + var mounts = Mounts{} + + scanner := bufio.NewScanner(fin) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + var mount = &Mount{ + fields[0], + fields[1], + fields[2], + fields[3], + } + mounts.Mounts = append(mounts.Mounts, *mount) + } + + if err := scanner.Err(); err != nil { + return nil, err + } + return &mounts, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/net_ip.go b/vendor/github.com/c9s/goprocinfo/linux/net_ip.go new file mode 100644 index 00000000..a94e2d0b --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/net_ip.go @@ -0,0 +1,173 @@ +package linux + +import ( + "errors" + "net" + "regexp" + "strconv" + "strings" +) + +var ( + ipv4RegExp = regexp.MustCompile("^[0-9a-fA-F]{8}:[0-9a-fA-F]{4}$") // Regex for NetIPv4Decoder + ipv6RegExp = regexp.MustCompile("^[0-9a-fA-F]{32}:[0-9a-fA-F]{4}$") // Regex for NetIPv6Decoder +) + +type NetIPDecoder func(string) (string, error) // Either NetIPv4Decoder or NetIPv6Decoder + +type NetSocket struct { + LocalAddress string `json:"local_address"` + RemoteAddress string `json:"remote_address"` + Status uint8 `json:"st"` + TxQueue uint64 `json:"tx_queue"` + RxQueue uint64 `json:"rx_queue"` + Uid uint32 `json:"uid"` + Inode uint64 `json:"inode"` + SocketReferenceCount uint64 `json:"ref"` +} + +func parseNetSocket(f []string, ip NetIPDecoder) (*NetSocket, error) { + + if len(f) < 11 { + return nil, errors.New("Cannot parse net socket line: " + strings.Join(f, " ")) + } + + if strings.Index(f[4], ":") == -1 { + return nil, errors.New("Cannot parse tx/rx queues: " + f[4]) + } + + q := strings.Split(f[4], ":") + + socket := &NetSocket{} + + var s uint64 // socket.Status + var u uint64 // socket.Uid + var err error // parse error + + if socket.LocalAddress, err = ip(f[1]); err != nil { + return nil, err + } + + if socket.RemoteAddress, err = ip(f[2]); err != nil { + return nil, err + } + + if s, err = strconv.ParseUint(f[3], 16, 8); err != nil { + return nil, err + } + + if socket.TxQueue, err = strconv.ParseUint(q[0], 16, 64); err != nil { + return nil, err + } + + if socket.RxQueue, err = strconv.ParseUint(q[1], 16, 64); err != nil { + return nil, err + } + + if u, err = strconv.ParseUint(f[7], 10, 32); err != nil { + return nil, err + } + + if socket.Inode, err = strconv.ParseUint(f[9], 10, 64); err != nil { + return nil, err + } + + if socket.SocketReferenceCount, err = strconv.ParseUint(f[10], 10, 64); err != nil { + return nil, err + } + + socket.Status = uint8(s) + socket.Uid = uint32(u) + + return socket, nil +} + +// NetIPv4Decoder decodes an IPv4 address with port from a given hex string +// NOTE: This function match NetIPDecoder type +func NetIPv4Decoder(s string) (string, error) { + + if !ipv4RegExp.MatchString(s) { + return "", errors.New("Cannot decode ipv4 address: " + s) + } + + i := strings.Split(s, ":") + + b := make([]byte, 4) + + for j := 0; j < 4; j++ { + + x := j * 2 + y := x + 2 + z := 3 - j + + // Extract 2 characters from hex string, 4 times. + // + // s: "0100007F" -> [ + // h: "01", h: "00", h: "00", h: "7F", + // ] + h := i[0][x:y] + + // Reverse byte order + n, _ := strconv.ParseUint(h, 16, 8) + b[z] = byte(n) + + } + + h := net.IP(b).String() + n, _ := strconv.ParseUint(i[1], 16, 64) + p := strconv.FormatUint(n, 10) + + // ipv4:port + v := h + ":" + p + + return v, nil +} + +// NetIPv6Decoder decodes an IPv6 address with port from a given hex string +// NOTE: This function match NetIPDecoder type +func NetIPv6Decoder(s string) (string, error) { + + if !ipv6RegExp.MatchString(s) { + return "", errors.New("Cannot decode ipv6 address: " + s) + } + + i := strings.Split(s, ":") + + b := make([]byte, 16) + + for j := 0; j < 4; j++ { + + x := j * 8 + y := x + 8 + + // Extract 8 characters from hex string, 4 times. + // + // s: "350E012A900F122E85EDEAADA64DAAD1" -> [ + // h: "350E012A", h: "900F122E", + // h: "85EDEAAD", h: "A64DAAD1", + // ] + h := i[0][x:y] + + for k := 0; k < 4; k++ { + + // Reverse byte order + // "350E012A" -> [ 0x2A, 0x01, 0x0E, 0x35 ] + z := (j * 4) + k + g := 8 - (k * 2) + f := g - 2 + + n, _ := strconv.ParseUint(h[f:g], 16, 8) + b[z] = byte(n) + + } + } + + h := net.IP(b).String() + n, _ := strconv.ParseUint(i[1], 16, 64) + p := strconv.FormatUint(n, 10) + + // ipv6:port + v := h + ":" + p + + return v, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/net_tcp.go b/vendor/github.com/c9s/goprocinfo/linux/net_tcp.go new file mode 100644 index 00000000..ae71a9d3 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/net_tcp.go @@ -0,0 +1,82 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +type NetTCPSockets struct { + Sockets []NetTCPSocket `json:"sockets"` +} + +type NetTCPSocket struct { + NetSocket + RetransmitTimeout uint64 `json:"retransmit_timeout"` + PredictedTick uint64 `json:"predicted_tick"` + AckQuick uint8 `json:"ack_quick"` + AckPingpong bool `json:"ack_pingpong"` + SendingCongestionWindow uint64 `json:"sending_congestion_window"` + SlowStartSizeThreshold int64 `json:"slow_start_size_threshold"` +} + +func ReadNetTCPSockets(path string, ip NetIPDecoder) (*NetTCPSockets, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(strings.TrimRight(string(b), "\n "), "\n") + + tcp := &NetTCPSockets{} + + for i := 1; i < len(lines); i++ { + + line := lines[i] + + f := strings.Fields(line) + + s, err := parseNetSocket(f, ip) + + if err != nil { + return nil, err + } + + var n int64 + e := &NetTCPSocket{ + NetSocket: *s, + } + + // Depending on socket state, the number of fields presented is either + // 12 or 17. + if len(f) >= 17 { + if e.RetransmitTimeout, err = strconv.ParseUint(f[12], 10, 64); err != nil { + return nil, err + } + + if e.PredictedTick, err = strconv.ParseUint(f[13], 10, 64); err != nil { + return nil, err + } + + if n, err = strconv.ParseInt(f[14], 10, 8); err != nil { + return nil, err + } + e.AckQuick = uint8(n >> 1) + e.AckPingpong = ((n & 1) == 1) + + if e.SendingCongestionWindow, err = strconv.ParseUint(f[15], 10, 64); err != nil { + return nil, err + } + + if e.SlowStartSizeThreshold, err = strconv.ParseInt(f[16], 10, 32); err != nil { + return nil, err + } + } + + tcp.Sockets = append(tcp.Sockets, *e) + } + + return tcp, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/net_udp.go b/vendor/github.com/c9s/goprocinfo/linux/net_udp.go new file mode 100644 index 00000000..b1b68e36 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/net_udp.go @@ -0,0 +1,59 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +type NetUDPSockets struct { + Sockets []NetUDPSocket `json:"sockets"` +} + +type NetUDPSocket struct { + NetSocket + Drops uint64 `json:"drops"` +} + +func ReadNetUDPSockets(path string, ip NetIPDecoder) (*NetUDPSockets, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(b), "\n") + + udp := &NetUDPSockets{} + + for i := 1; i < len(lines); i++ { + + line := lines[i] + + f := strings.Fields(line) + + if len(f) < 13 { + continue + } + + s, err := parseNetSocket(f, ip) + + if err != nil { + return nil, err + } + + e := &NetUDPSocket{ + NetSocket: *s, + Drops: 0, + } + + if e.Drops, err = strconv.ParseUint(f[12], 10, 64); err != nil { + return nil, err + } + + udp.Sockets = append(udp.Sockets, *e) + } + + return udp, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/net_unix.go b/vendor/github.com/c9s/goprocinfo/linux/net_unix.go new file mode 100644 index 00000000..b2958eef --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/net_unix.go @@ -0,0 +1,72 @@ +package linux + +import ( + "errors" + "io/ioutil" + "strconv" + "strings" +) + +type NetUnixDomainSockets struct { + Sockets []NetUnixDomainSocket `json:"sockets"` +} + +type NetUnixDomainSocket struct { + Protocol uint64 `json:"protocol"` + RefCount uint64 `json:"ref_count"` + Flags uint64 `json:"flags"` + Type uint64 `json:"type"` + State uint64 `json:"state"` + Inode uint64 `json:"inode"` + Path string `json:"path"` +} + +// ReadNetUnixDomainSockets parser to /proc/net/unix +func ReadNetUnixDomainSockets(fpath string) (*NetUnixDomainSockets, error) { + b, err := ioutil.ReadFile(fpath) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(b), "\n") + unixDomainSockets := &NetUnixDomainSockets{} + + for i := 1; i < len(lines); i++ { + line := lines[i] + f := strings.Fields(line) + + if len(f) < 8 { + continue + } + + socket := NetUnixDomainSocket{} + if socket.RefCount, err = strconv.ParseUint(f[1], 16, 64); err != nil { + return nil, errors.New("Cannot parse unix domain socket [invalid RefCount]: " + f[1]) + } + + if socket.Protocol, err = strconv.ParseUint(f[2], 10, 64); err != nil { + return nil, errors.New("Cannot parse unix domain socket [invalid Protocol]: " + f[2]) + } + + if socket.Flags, err = strconv.ParseUint(f[3], 10, 64); err != nil { + return nil, errors.New("Cannot parse unix domain socket [invalid Flags]: " + f[3]) + } + + if socket.Type, err = strconv.ParseUint(f[4], 10, 64); err != nil { + return nil, errors.New("Cannot parse unix domain socket [invalid Type]: " + f[4]) + } + + if socket.State, err = strconv.ParseUint(f[5], 10, 64); err != nil { + return nil, errors.New("Cannot parse unix domain socket [invalid State]: " + f[5]) + } + + if socket.Inode, err = strconv.ParseUint(f[6], 10, 64); err != nil { + return nil, errors.New("Cannot parse unix domain socket [invalid Inode]: " + f[6]) + } + + socket.Path = f[7] + unixDomainSockets.Sockets = append(unixDomainSockets.Sockets, socket) + } + return unixDomainSockets, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/netstat.go b/vendor/github.com/c9s/goprocinfo/linux/netstat.go new file mode 100644 index 00000000..d4a34590 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/netstat.go @@ -0,0 +1,174 @@ +package linux + +import ( + "io/ioutil" + "reflect" + "strconv" + "strings" +) + +type NetStat struct { + // TcpExt + SyncookiesSent uint64 `json:"syncookie_sent"` + SyncookiesRecv uint64 `json:"syncookies_recv"` + SyncookiesFailed uint64 `json:"syncookies_failed"` + EmbryonicRsts uint64 `json:"embryonic_rsts"` + PruneCalled uint64 `json:"prune_called"` + RcvPruned uint64 `json:"rcv_pruned"` + OfoPruned uint64 `json:"ofo_pruned"` + OutOfWindowIcmps uint64 `json:"out_of_window_icmps"` + LockDroppedIcmps uint64 `json:"lock_dropped_icmps"` + ArpFilter uint64 `json:"arp_filter"` + TW uint64 `json:"tw"` + TWRecycled uint64 `json:"tw_recycled"` + TWKilled uint64 `json:"tw_killed"` + PAWSPassive uint64 `json:"paws_passive"` + PAWSActive uint64 `json:"paws_active"` + PAWSEstab uint64 `json:"paws_estab"` + DelayedACKs uint64 `json:"delayed_acks"` + DelayedACKLocked uint64 `json:"delayed_ack_locked"` + DelayedACKLost uint64 `json:"delayed_ack_lost"` + ListenOverflows uint64 `json:"listen_overflows"` + ListenDrops uint64 `json:"listen_drops"` + TCPPrequeued uint64 `json:"tcp_prequeued"` + TCPDirectCopyFromBacklog uint64 `json:"tcp_direct_copy_from_backlog"` + TCPDirectCopyFromPrequeue uint64 `json:"tcp_direct_copy_from_prequeue"` + TCPPrequeueDropped uint64 `json:"tcp_prequeue_dropped"` + TCPHPHits uint64 `json:"tcp_hp_hits"` + TCPHPHitsToUser uint64 `json:"tcp_hp_hits_to_user"` + TCPPureAcks uint64 `json:"tcp_pure_acks"` + TCPHPAcks uint64 `json:"tcp_hp_acks"` + TCPRenoRecovery uint64 `json:"tcp_reno_recovery"` + TCPSackRecovery uint64 `json:"tcp_sack_recovery"` + TCPSACKReneging uint64 `json:"tcp_sack_reneging"` + TCPFACKReorder uint64 `json:"tcp_fack_reorder"` + TCPSACKReorder uint64 `json:"tcp_sack_reorder"` + TCPRenoReorder uint64 `json:"tcp_reno_reorder"` + TCPTSReorder uint64 `json:"tcp_ts_reorder"` + TCPFullUndo uint64 `json:"tcp_full_undo"` + TCPPartialUndo uint64 `json:"tcp_partial_undo"` + TCPDSACKUndo uint64 `json:"tcp_dsack_undo"` + TCPLossUndo uint64 `json:"tcp_loss_undo"` + TCPLoss uint64 `json:"tcp_loss"` + TCPLostRetransmit uint64 `json:"tcp_lost_retransmit"` + TCPRenoFailures uint64 `json:"tcp_reno_failures"` + TCPSackFailures uint64 `json:"tcp_sack_failures"` + TCPLossFailures uint64 `json:"tcp_loss_failures"` + TCPFastRetrans uint64 `json:"tcp_fast_retrans"` + TCPForwardRetrans uint64 `json:"tcp_forward_retrans"` + TCPSlowStartRetrans uint64 `json:"tcp_slow_start_retrans"` + TCPTimeouts uint64 `json:"tcp_timeouts"` + TCPLossProbes uint64 `json:"tcp_loss_probes"` + TCPLossProbeRecovery uint64 `json:"tcp_loss_probe_recovery"` + TCPRenoRecoveryFail uint64 `json:"tcp_reno_recovery_fail"` + TCPSackRecoveryFail uint64 `json:"tcp_sack_recovery_fail"` + TCPSchedulerFailed uint64 `json:"tcp_scheduler_failed"` + TCPRcvCollapsed uint64 `json:"tcp_rcv_collapsed"` + TCPDSACKOldSent uint64 `json:"tcp_dsack_old_sent"` + TCPDSACKOfoSent uint64 `json:"tcp_dsack_ofo_sent"` + TCPDSACKRecv uint64 `json:"tcp_dsack_recv"` + TCPDSACKOfoRecv uint64 `json:"tcp_dsack_ofo_recv"` + TCPAbortOnSyn uint64 `json:"tcp_abort_on_syn"` + TCPAbortOnData uint64 `json:"tcp_abort_on_data"` + TCPAbortOnClose uint64 `json:"tcp_abort_on_close"` + TCPAbortOnMemory uint64 `json:"tcp_abort_on_memory"` + TCPAbortOnTimeout uint64 `json:"tcp_abort_on_timeout"` + TCPAbortOnLinger uint64 `json:"tcp_abort_on_linger"` + TCPAbortFailed uint64 `json:"tcp_abort_failed"` + TCPMemoryPressures uint64 `json:"tcp_memory_pressures"` + TCPSACKDiscard uint64 `json:"tcp_sack_discard"` + TCPDSACKIgnoredOld uint64 `json:"tcp_dsack_ignored_old"` + TCPDSACKIgnoredNoUndo uint64 `json:"tcp_dsack_ignored_no_undo"` + TCPSpuriousRTOs uint64 `json:"tcp_spurious_rtos"` + TCPMD5NotFound uint64 `json:"tcp_md5_not_found"` + TCPMD5Unexpected uint64 `json:"tcp_md5_unexpected"` + TCPSackShifted uint64 `json:"tcp_sack_shifted"` + TCPSackMerged uint64 `json:"tcp_sack_merged"` + TCPSackShiftFallback uint64 `json:"tcp_sack_shift_fallback"` + TCPBacklogDrop uint64 `json:"tcp_backlog_drop"` + TCPMinTTLDrop uint64 `json:"tcp_min_ttl_drop"` + TCPDeferAcceptDrop uint64 `json:"tcp_defer_accept_drop"` + IPReversePathFilter uint64 `json:"ip_reverse_path_filter"` + TCPTimeWaitOverflow uint64 `json:"tcp_time_wait_overflow"` + TCPReqQFullDoCookies uint64 `json:"tcp_req_q_full_do_cookies"` + TCPReqQFullDrop uint64 `json:"tcp_req_q_full_drop"` + TCPRetransFail uint64 `json:"tcp_retrans_fail"` + TCPRcvCoalesce uint64 `json:"tcp_rcv_coalesce"` + TCPOFOQueue uint64 `json:"tcp_ofo_queue"` + TCPOFODrop uint64 `json:"tcp_ofo_drop"` + TCPOFOMerge uint64 `json:"tcp_ofo_merge"` + TCPChallengeACK uint64 `json:"tcp_challenge_ack"` + TCPSYNChallenge uint64 `json:"tcp_syn_challenge"` + TCPFastOpenActive uint64 `json:"tcp_fast_open_active"` + TCPFastOpenActiveFail uint64 `json:"tcp_fast_open_active_fail"` + TCPFastOpenPassive uint64 `json:"tcp_fast_open_passive"` + TCPFastOpenPassiveFail uint64 `json:"tcp_fast_open_passive_fail"` + TCPFastOpenListenOverflow uint64 `json:"tcp_fast_open_listen_overflow"` + TCPFastOpenCookieReqd uint64 `json:"tcp_fast_open_cookie_reqd"` + TCPSpuriousRtxHostQueues uint64 `json:"tcp_spurious_rtx_host_queues"` + BusyPollRxPackets uint64 `json:"busy_poll_rx_packets"` + TCPAutoCorking uint64 `json:"tcp_auto_corking"` + TCPFromZeroWindowAdv uint64 `json:"tcp_from_zero_window_adv"` + TCPToZeroWindowAdv uint64 `json:"tcp_to_zero_window_adv"` + TCPWantZeroWindowAdv uint64 `json:"tcp_want_zero_window_adv"` + TCPSynRetrans uint64 `json:"tcp_syn_retrans"` + TCPOrigDataSent uint64 `json:"tcp_orig_data_sent"` + // IpExt + InNoRoutes uint64 `json:"in_no_routes"` + InTruncatedPkts uint64 `json:"in_truncated_pkts"` + InMcastPkts uint64 `json:"in_mcast_pkts"` + OutMcastPkts uint64 `json:"out_mcast_pkts"` + InBcastPkts uint64 `json:"in_bcast_pkts"` + OutBcastPkts uint64 `json:"out_bcast_pkts"` + InOctets uint64 `json:"in_octets"` + OutOctets uint64 `json:"out_octets"` + InMcastOctets uint64 `json:"in_mcast_octets"` + OutMcastOctets uint64 `json:"out_mcast_octets"` + InBcastOctets uint64 `json:"in_bcast_octets"` + OutBcastOctets uint64 `json:"out_bcast_octets"` + InCsumErrors uint64 `json:"in_csum_errors"` + InNoECTPkts uint64 `json:"in_no_ect_pkts"` + InECT1Pkts uint64 `json:"in_ect1_pkts"` + InECT0Pkts uint64 `json:"in_ect0_pkts"` + InCEPkts uint64 `json:"in_ce_pkts"` +} + +func ReadNetStat(path string) (*NetStat, error) { + data, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(data), "\n") + + // Maps a netstat metric to its value (i.e. SyncookiesSent --> 0) + statMap := make(map[string]string) + + // patterns + // TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed... <-- header + // TcpExt: 0 0 1764... <-- values + + for i := 1; i < len(lines); i = i + 2 { + headers := strings.Fields(lines[i-1][strings.Index(lines[i-1], ":")+1:]) + values := strings.Fields(lines[i][strings.Index(lines[i], ":")+1:]) + + for j, header := range headers { + statMap[header] = values[j] + } + } + + var netstat NetStat = NetStat{} + + elem := reflect.ValueOf(&netstat).Elem() + typeOfElem := elem.Type() + + for i := 0; i < elem.NumField(); i++ { + if val, ok := statMap[typeOfElem.Field(i).Name]; ok { + parsedVal, _ := strconv.ParseUint(val, 10, 64) + elem.Field(i).SetUint(parsedVal) + } + } + + return &netstat, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/network_stat.go b/vendor/github.com/c9s/goprocinfo/linux/network_stat.go new file mode 100644 index 00000000..59093695 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/network_stat.go @@ -0,0 +1,73 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +type NetworkStat struct { + Iface string `json:"iface"` + RxBytes uint64 `json:"rxbytes"` + RxPackets uint64 `json:"rxpackets"` + RxErrs uint64 `json:"rxerrs"` + RxDrop uint64 `json:"rxdrop"` + RxFifo uint64 `json:"rxfifo"` + RxFrame uint64 `json:"rxframe"` + RxCompressed uint64 `json:"rxcompressed"` + RxMulticast uint64 `json:"rxmulticast"` + TxBytes uint64 `json:"txbytes"` + TxPackets uint64 `json:"txpackets"` + TxErrs uint64 `json:"txerrs"` + TxDrop uint64 `json:"txdrop"` + TxFifo uint64 `json:"txfifo"` + TxColls uint64 `json:"txcolls"` + TxCarrier uint64 `json:"txcarrier"` + TxCompressed uint64 `json:"txcompressed"` +} + +func ReadNetworkStat(path string) ([]NetworkStat, error) { + data, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(data), "\n") + + // lines[2:] remove /proc/net/dev header + results := make([]NetworkStat, len(lines[2:])-1) + + for i, line := range lines[2:] { + // patterns + // : 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // or + // :0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (without space after colon) + colon := strings.Index(line, ":") + + if colon > 0 { + metrics := line[colon+1:] + fields := strings.Fields(metrics) + + results[i].Iface = strings.Replace(line[0:colon], " ", "", -1) + results[i].RxBytes, _ = strconv.ParseUint(fields[0], 10, 64) + results[i].RxPackets, _ = strconv.ParseUint(fields[1], 10, 64) + results[i].RxErrs, _ = strconv.ParseUint(fields[2], 10, 64) + results[i].RxDrop, _ = strconv.ParseUint(fields[3], 10, 64) + results[i].RxFifo, _ = strconv.ParseUint(fields[4], 10, 64) + results[i].RxFrame, _ = strconv.ParseUint(fields[5], 10, 64) + results[i].RxCompressed, _ = strconv.ParseUint(fields[6], 10, 64) + results[i].RxMulticast, _ = strconv.ParseUint(fields[7], 10, 64) + results[i].TxBytes, _ = strconv.ParseUint(fields[8], 10, 64) + results[i].TxPackets, _ = strconv.ParseUint(fields[9], 10, 64) + results[i].TxErrs, _ = strconv.ParseUint(fields[10], 10, 64) + results[i].TxDrop, _ = strconv.ParseUint(fields[11], 10, 64) + results[i].TxFifo, _ = strconv.ParseUint(fields[12], 10, 64) + results[i].TxColls, _ = strconv.ParseUint(fields[13], 10, 64) + results[i].TxCarrier, _ = strconv.ParseUint(fields[14], 10, 64) + results[i].TxCompressed, _ = strconv.ParseUint(fields[15], 10, 64) + } + } + + return results, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process.go b/vendor/github.com/c9s/goprocinfo/linux/process.go new file mode 100644 index 00000000..4fd062b7 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process.go @@ -0,0 +1,62 @@ +package linux + +import ( + "os" + "path/filepath" + "strconv" +) + +type Process struct { + Status ProcessStatus `json:"status"` + Statm ProcessStatm `json:"statm"` + Stat ProcessStat `json:"stat"` + IO ProcessIO `json:"io"` + Cmdline string `json:"cmdline"` +} + +func ReadProcess(pid uint64, path string) (*Process, error) { + + var err error + + p := filepath.Join(path, strconv.FormatUint(pid, 10)) + + if _, err = os.Stat(p); err != nil { + return nil, err + } + + process := Process{} + + var io *ProcessIO + var stat *ProcessStat + var statm *ProcessStatm + var status *ProcessStatus + var cmdline string + + if io, err = ReadProcessIO(filepath.Join(p, "io")); err != nil { + return nil, err + } + + if stat, err = ReadProcessStat(filepath.Join(p, "stat")); err != nil { + return nil, err + } + + if statm, err = ReadProcessStatm(filepath.Join(p, "statm")); err != nil { + return nil, err + } + + if status, err = ReadProcessStatus(filepath.Join(p, "status")); err != nil { + return nil, err + } + + if cmdline, err = ReadProcessCmdline(filepath.Join(p, "cmdline")); err != nil { + return nil, err + } + + process.IO = *io + process.Stat = *stat + process.Statm = *statm + process.Status = *status + process.Cmdline = cmdline + + return &process, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_cmdline.go b/vendor/github.com/c9s/goprocinfo/linux/process_cmdline.go new file mode 100644 index 00000000..4fc6afe7 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_cmdline.go @@ -0,0 +1,39 @@ +package linux + +import ( + "io/ioutil" + "strings" +) + +func ReadProcessCmdline(path string) (string, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return "", err + } + + l := len(b) - 1 // Define limit before last byte ('\0') + z := byte(0) // '\0' or null byte + s := byte(0x20) // space byte + c := 0 // cursor of useful bytes + + for i := 0; i < l; i++ { + + // Check if next byte is not a '\0' byte. + if b[i+1] != z { + + // Offset must match a '\0' byte. + c = i + 2 + + // If current byte is '\0', replace it with a space byte. + if b[i] == z { + b[i] = s + } + } + } + + x := strings.TrimSpace(string(b[0:c])) + + return x, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_io.go b/vendor/github.com/c9s/goprocinfo/linux/process_io.go new file mode 100644 index 00000000..8106a1e4 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_io.go @@ -0,0 +1,71 @@ +package linux + +import ( + "io/ioutil" + "reflect" + "strconv" + "strings" +) + +// I/O statistics for the process. +type ProcessIO struct { + RChar uint64 `json:"rchar" field:"rchar"` // chars read + WChar uint64 `json:"wchar" field:"wchar"` // chars written + Syscr uint64 `json:"syscr" field:"syscr"` // read syscalls + Syscw uint64 `json:"syscw" field:"syscw"` // write syscalls + ReadBytes uint64 `json:"read_bytes" field:"read_bytes"` // bytes read + WriteBytes uint64 `json:"write_bytes" field:"write_bytes"` // bytes written + CancelledWriteBytes uint64 `json:"cancelled_write_bytes" field:"cancelled_write_bytes"` // bytes truncated +} + +func ReadProcessIO(path string) (*ProcessIO, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + // Maps a io metric to its value (i.e. rchar --> 100000) + m := map[string]uint64{} + + io := ProcessIO{} + + lines := strings.Split(string(b), "\n") + + for _, line := range lines { + + if strings.Index(line, ": ") == -1 { + continue + } + + l := strings.Split(line, ": ") + + k := l[0] + v, err := strconv.ParseUint(l[1], 10, 64) + + if err != nil { + return nil, err + } + + m[k] = v + + } + + e := reflect.ValueOf(&io).Elem() + t := e.Type() + + for i := 0; i < e.NumField(); i++ { + + k := t.Field(i).Tag.Get("field") + + v, ok := m[k] + + if ok { + e.Field(i).SetUint(v) + } + + } + + return &io, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_pid.go b/vendor/github.com/c9s/goprocinfo/linux/process_pid.go new file mode 100644 index 00000000..8085d25f --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_pid.go @@ -0,0 +1,54 @@ +package linux + +import ( + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" +) + +func ReadMaxPID(path string) (uint64, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return 0, err + } + + s := strings.TrimSpace(string(b)) + + i, err := strconv.ParseUint(s, 10, 64) + + if err != nil { + return 0, err + } + + return i, nil + +} + +func ListPID(path string, max uint64) ([]uint64, error) { + + l := make([]uint64, 0, 5) + + for i := uint64(1); i <= max; i++ { + + p := filepath.Join(path, strconv.FormatUint(i, 10)) + + s, err := os.Stat(p) + + if err != nil && !os.IsNotExist(err) { + return nil, err + } + + if err != nil || !s.IsDir() { + continue + } + + l = append(l, i) + + } + + return l, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_sched_stat.go b/vendor/github.com/c9s/goprocinfo/linux/process_sched_stat.go new file mode 100644 index 00000000..e6343fde --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_sched_stat.go @@ -0,0 +1,46 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +// https://www.kernel.org/doc/Documentation/scheduler/sched-stats.txt +type ProcessSchedStat struct { + RunTime uint64 `json:"run_time"` // time spent on the cpu + RunqueueTime uint64 `json:"runqueue_time"` // time spent waiting on a runqueue + RunPeriods uint64 `json:"run_periods"` // # of timeslices run on this cpu +} + +func ReadProcessSchedStat(path string) (*ProcessSchedStat, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + s := string(b) + f := strings.Fields(s) + + schedStat := ProcessSchedStat{} + + var n uint64 + + for i := 0; i < len(f); i++ { + + if n, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + + switch i { + case 0: + schedStat.RunTime = n + case 1: + schedStat.RunqueueTime = n + case 2: + schedStat.RunPeriods = n + } + + } + return &schedStat, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_stat.go b/vendor/github.com/c9s/goprocinfo/linux/process_stat.go new file mode 100644 index 00000000..5343cbde --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_stat.go @@ -0,0 +1,303 @@ +package linux + +import ( + "io/ioutil" + "regexp" + "strconv" + "strings" +) + +// Status information about the process. +type ProcessStat struct { + Pid uint64 `json:"pid"` + Comm string `json:"comm"` + State string `json:"state"` + Ppid int64 `json:"ppid"` + Pgrp int64 `json:"pgrp"` + Session int64 `json:"session"` + TtyNr int64 `json:"tty_nr"` + Tpgid int64 `json:"tpgid"` + Flags uint64 `json:"flags"` + Minflt uint64 `json:"minflt"` + Cminflt uint64 `json:"cminflt"` + Majflt uint64 `json:"majflt"` + Cmajflt uint64 `json:"cmajflt"` + Utime uint64 `json:"utime"` + Stime uint64 `json:"stime"` + Cutime int64 `json:"cutime"` + Cstime int64 `json:"cstime"` + Priority int64 `json:"priority"` + Nice int64 `json:"nice"` + NumThreads int64 `json:"num_threads"` + Itrealvalue int64 `json:"itrealvalue"` + Starttime uint64 `json:"starttime"` + Vsize uint64 `json:"vsize"` + Rss int64 `json:"rss"` + Rsslim uint64 `json:"rsslim"` + Startcode uint64 `json:"startcode"` + Endcode uint64 `json:"endcode"` + Startstack uint64 `json:"startstack"` + Kstkesp uint64 `json:"kstkesp"` + Kstkeip uint64 `json:"kstkeip"` + Signal uint64 `json:"signal"` + Blocked uint64 `json:"blocked"` + Sigignore uint64 `json:"sigignore"` + Sigcatch uint64 `json:"sigcatch"` + Wchan uint64 `json:"wchan"` + Nswap uint64 `json:"nswap"` + Cnswap uint64 `json:"cnswap"` + ExitSignal int64 `json:"exit_signal"` + Processor int64 `json:"processor"` + RtPriority uint64 `json:"rt_priority"` + Policy uint64 `json:"policy"` + DelayacctBlkioTicks uint64 `json:"delayacct_blkio_ticks"` + GuestTime uint64 `json:"guest_time"` + CguestTime int64 `json:"cguest_time"` + StartData uint64 `json:"start_data"` + EndData uint64 `json:"end_data"` + StartBrk uint64 `json:"start_brk"` + ArgStart uint64 `json:"arg_start"` + ArgEnd uint64 `json:"arg_end"` + EnvStart uint64 `json:"env_start"` + EnvEnd uint64 `json:"env_end"` + ExitCode int64 `json:"exit_code"` +} + +var processStatRegExp = regexp.MustCompile("^(\\d+)( \\(.*?\\) )(.*)$") + +func ReadProcessStat(path string) (*ProcessStat, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + s := string(b) + + f := make([]string, 0, 32) + + e := processStatRegExp.FindStringSubmatch(strings.TrimSpace(s)) + + // Inject process Pid + f = append(f, e[1]) + + // Inject process Comm + f = append(f, strings.TrimSpace(e[2])) + + // Inject all remaining process info + f = append(f, (strings.Fields(e[3]))...) + + stat := ProcessStat{} + + for i := 0; i < len(f); i++ { + switch i { + case 0: + if stat.Pid, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 1: + stat.Comm = f[i] + case 2: + stat.State = f[i] + case 3: + if stat.Ppid, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 4: + if stat.Pgrp, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 5: + if stat.Session, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 6: + if stat.TtyNr, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 7: + if stat.Tpgid, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 8: + if stat.Flags, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 9: + if stat.Minflt, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 10: + if stat.Cminflt, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 11: + if stat.Majflt, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 12: + if stat.Cmajflt, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 13: + if stat.Utime, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 14: + if stat.Stime, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 15: + if stat.Cutime, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 16: + if stat.Cstime, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 17: + if stat.Priority, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 18: + if stat.Nice, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 19: + if stat.NumThreads, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 20: + if stat.Itrealvalue, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 21: + if stat.Starttime, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 22: + if stat.Vsize, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 23: + if stat.Rss, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 24: + if stat.Rsslim, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 25: + if stat.Startcode, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 26: + if stat.Endcode, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 27: + if stat.Startstack, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 28: + if stat.Kstkesp, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 29: + if stat.Kstkeip, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 30: + if stat.Signal, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 31: + if stat.Blocked, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 32: + if stat.Sigignore, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 33: + if stat.Sigcatch, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 34: + if stat.Wchan, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 35: + if stat.Nswap, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 36: + if stat.Cnswap, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 37: + if stat.ExitSignal, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 38: + if stat.Processor, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 39: + if stat.RtPriority, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 40: + if stat.Policy, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 41: + if stat.DelayacctBlkioTicks, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 42: + if stat.GuestTime, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 43: + if stat.CguestTime, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 44: + if stat.StartData, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 45: + if stat.EndData, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 46: + if stat.StartBrk, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 47: + if stat.ArgStart, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 48: + if stat.ArgEnd, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 49: + if stat.EnvStart, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 50: + if stat.EnvEnd, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 51: + if stat.ExitCode, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + } + } + + return &stat, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_statm.go b/vendor/github.com/c9s/goprocinfo/linux/process_statm.go new file mode 100644 index 00000000..8720cdf1 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_statm.go @@ -0,0 +1,61 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +// Provides information about memory usage, measured in pages. +type ProcessStatm struct { + Size uint64 `json:"size"` // total program size + Resident uint64 `json:"resident"` // resident set size + Share uint64 `json:"share"` // shared pages + Text uint64 `json:"text"` // text (code) + Lib uint64 `json:"lib"` // library (unused in Linux 2.6) + Data uint64 `json:"data"` // data + stack + Dirty uint64 `json:"dirty"` // dirty pages (unused in Linux 2.6) +} + +func ReadProcessStatm(path string) (*ProcessStatm, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + s := string(b) + f := strings.Fields(s) + + statm := ProcessStatm{} + + var n uint64 + + for i := 0; i < len(f); i++ { + + if n, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + + switch i { + case 0: + statm.Size = n + case 1: + statm.Resident = n + case 2: + statm.Share = n + case 3: + statm.Text = n + case 4: + statm.Lib = n + case 5: + statm.Data = n + case 6: + statm.Dirty = n + } + + } + + return &statm, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_status.go b/vendor/github.com/c9s/goprocinfo/linux/process_status.go new file mode 100644 index 00000000..8441806b --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_status.go @@ -0,0 +1,331 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +// Provides much of the information from ProcessStatm and ProcessStat +type ProcessStatus struct { + Name string + State string + Tgid uint64 + Pid uint64 + PPid int64 + TracerPid uint64 + RealUid uint64 + EffectiveUid uint64 + SavedSetUid uint64 + FilesystemUid uint64 + RealGid uint64 + EffectiveGid uint64 + SavedSetGid uint64 + FilesystemGid uint64 + FDSize uint64 + Groups []int64 + VmPeak uint64 + VmSize uint64 + VmLck uint64 + VmHWM uint64 + VmRSS uint64 + VmData uint64 + VmStk uint64 + VmExe uint64 + VmLib uint64 + VmPTE uint64 + VmSwap uint64 + Threads uint64 + SigQLength uint64 + SigQLimit uint64 + SigPnd uint64 + ShdPnd uint64 + SigBlk uint64 + SigIgn uint64 + SigCgt uint64 + CapInh uint64 + CapPrm uint64 + CapEff uint64 + CapBnd uint64 + Seccomp uint8 + CpusAllowed []uint32 + MemsAllowed []uint32 + VoluntaryCtxtSwitches uint64 + NonvoluntaryCtxtSwitches uint64 +} + +func ReadProcessStatus(path string) (*ProcessStatus, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + status := ProcessStatus{} + + lines := strings.Split(string(b), "\n") + + for _, line := range lines { + + if strings.Index(line, ":") == -1 { + continue + } + + l := strings.Split(line, ":") + + k := strings.TrimSpace(l[0]) + v := strings.TrimSpace(l[1]) + + switch k { + case "Name": + status.Name = v + case "State": + status.State = v + case "Tgid": + if status.Tgid, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "Pid": + if status.Pid, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "PPid": + if status.PPid, err = strconv.ParseInt(v, 10, 64); err != nil { + return nil, err + } + case "TracerPid": + if status.TracerPid, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "Uid": + if f := strings.Fields(v); len(f) == 4 { + if status.RealUid, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + if status.EffectiveUid, err = strconv.ParseUint(f[1], 10, 64); err != nil { + return nil, err + } + if status.SavedSetUid, err = strconv.ParseUint(f[2], 10, 64); err != nil { + return nil, err + } + if status.FilesystemUid, err = strconv.ParseUint(f[3], 10, 64); err != nil { + return nil, err + } + } + case "Gid": + if f := strings.Fields(v); len(f) == 4 { + if status.RealGid, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + if status.EffectiveGid, err = strconv.ParseUint(f[1], 10, 64); err != nil { + return nil, err + } + if status.SavedSetGid, err = strconv.ParseUint(f[2], 10, 64); err != nil { + return nil, err + } + if status.FilesystemGid, err = strconv.ParseUint(f[3], 10, 64); err != nil { + return nil, err + } + } + case "FDSize": + if status.FDSize, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "Groups": + { + + f := strings.Fields(v) + status.Groups = make([]int64, len(f)) + + for i := range status.Groups { + if status.Groups[i], err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + } + + } + case "VmPeak": + { + f := strings.Fields(v) + if status.VmPeak, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmSize": + { + f := strings.Fields(v) + if status.VmSize, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmLck": + { + f := strings.Fields(v) + if status.VmLck, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmHWM": + { + f := strings.Fields(v) + if status.VmHWM, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmRSS": + { + f := strings.Fields(v) + if status.VmRSS, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmData": + { + f := strings.Fields(v) + if status.VmData, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmStk": + { + f := strings.Fields(v) + if status.VmStk, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmExe": + { + f := strings.Fields(v) + if status.VmExe, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmLib": + { + f := strings.Fields(v) + if status.VmLib, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmPTE": + { + f := strings.Fields(v) + if status.VmPTE, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmSwap": + { + f := strings.Fields(v) + if status.VmSwap, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "Threads": + if status.Threads, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "SigQ": + { + if f := strings.Split(v, "/"); len(f) == 2 { + if status.SigQLength, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + if status.SigQLimit, err = strconv.ParseUint(f[1], 10, 64); err != nil { + return nil, err + } + } + } + case "SigPnd": + if status.SigPnd, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "ShdPnd": + if status.ShdPnd, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "SigBlk": + if status.SigBlk, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "SigIgn": + if status.SigIgn, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "SigCgt": + if status.SigCgt, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "CapInh": + if status.CapInh, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "CapPrm": + if status.CapPrm, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "CapEff": + if status.CapEff, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "CapBnd": + if status.CapBnd, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "Seccomp": + { + + var n uint64 + + if n, err = strconv.ParseUint(v, 10, 8); err != nil { + return nil, err + } + + status.Seccomp = uint8(n) + } + case "Cpus_allowed": + { + + var n uint64 + + f := strings.Split(v, ",") + status.CpusAllowed = make([]uint32, len(f)) + + for i := range status.CpusAllowed { + if n, err = strconv.ParseUint(f[i], 16, 32); err != nil { + return nil, err + } + status.CpusAllowed[i] = uint32(n) + } + + } + case "Mems_allowed": + { + + var n uint64 + + f := strings.Split(v, ",") + status.MemsAllowed = make([]uint32, len(f)) + + for i := range status.MemsAllowed { + if n, err = strconv.ParseUint(f[i], 16, 32); err != nil { + return nil, err + } + status.MemsAllowed[i] = uint32(n) + } + + } + case "voluntary_ctxt_switches": + if status.VoluntaryCtxtSwitches, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "nonvoluntary_ctxt_switches": + if status.NonvoluntaryCtxtSwitches, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + } + } + + return &status, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/snmp.go b/vendor/github.com/c9s/goprocinfo/linux/snmp.go new file mode 100644 index 00000000..c576cef6 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/snmp.go @@ -0,0 +1,151 @@ +package linux + +import ( + "io/ioutil" + "reflect" + "strconv" + "strings" +) + +type Snmp struct { + // Ip + IpForwarding uint64 `json:"ip_forwarding"` + IpDefaultTTL uint64 `json:"ip_default_ttl"` + IpInReceives uint64 `json:"ip_in_receives"` + IpInHdrErrors uint64 `json:"ip_in_hdr_errors"` + IpInAddrErrors uint64 `json:"ip_in_addr_errors"` + IpForwDatagrams uint64 `json:"ip_forw_datagrams"` + IpInUnknownProtos uint64 `json:"ip_in_unknown_protos"` + IpInDiscards uint64 `json:"ip_in_discards"` + IpInDelivers uint64 `json:"ip_in_delivers"` + IpOutRequests uint64 `json:"ip_out_requests"` + IpOutDiscards uint64 `json:"ip_out_discards"` + IpOutNoRoutes uint64 `json:"ip_out_no_routes"` + IpReasmTimeout uint64 `json:"ip_reasm_timeout"` + IpReasmReqds uint64 `json:"ip_reasm_reqds"` + IpReasmOKs uint64 `json:"ip_reasm_oks"` + IpReasmFails uint64 `json:"ip_reasm_fails"` + IpFragOKs uint64 `json:"ip_frag_oks"` + IpFragFails uint64 `json:"ip_frag_fails"` + IpFragCreates uint64 `json:"ip_frag_creates"` + // Icmp + IcmpInMsgs uint64 `json:"icmp_in_msgs"` + IcmpInErrors uint64 `json:"icmp_in_errors"` + IcmpInCsumErrors uint64 `json:"icmp_in_csum_errors"` + IcmpInDestUnreachs uint64 `json:"icmp_in_dest_unreachs"` + IcmpInTimeExcds uint64 `json:"icmp_in_time_excds"` + IcmpInParmProbs uint64 `json:"icmp_in_parm_probs"` + IcmpInSrcQuenchs uint64 `json:"icmp_in_src_quenchs"` + IcmpInRedirects uint64 `json:"icmp_in_redirects"` + IcmpInEchos uint64 `json:"icmp_in_echos"` + IcmpInEchoReps uint64 `json:"icmp_in_echo_reps"` + IcmpInTimestamps uint64 `json:"icmp_in_timestamps"` + IcmpInTimestampReps uint64 `json:"icmp_in_timestamp_reps"` + IcmpInAddrMasks uint64 `json:"icmp_in_addr_masks"` + IcmpInAddrMaskReps uint64 `json:"icmp_in_addr_mask_reps"` + IcmpOutMsgs uint64 `json:"icmp_out_msgs"` + IcmpOutErrors uint64 `json:"icmp_out_errors"` + IcmpOutDestUnreachs uint64 `json:"icmp_out_dest_unreachs"` + IcmpOutTimeExcds uint64 `json:"icmp_out_time_excds"` + IcmpOutParmProbs uint64 `json:"icmp_out_parm_probs"` + IcmpOutSrcQuenchs uint64 `json:"icmp_out_src_quenchs"` + IcmpOutRedirects uint64 `json:"icmp_out_redirects"` + IcmpOutEchos uint64 `json:"icmp_out_echos"` + IcmpOutEchoReps uint64 `json:"icmp_out_echo_reps"` + IcmpOutTimestamps uint64 `json:"icmp_out_timestamps"` + IcmpOutTimestampReps uint64 `json:"icmp_out_timestamp_reps"` + IcmpOutAddrMasks uint64 `json:"icmp_out_addr_masks"` + IcmpOutAddrMaskReps uint64 `json:"icmp_out_addr_mask_reps"` + // IcmpMsg + IcmpMsgInType0 uint64 `json:"icmpmsg_in_type0"` + IcmpMsgInType3 uint64 `json:"icmpmsg_in_type3"` + IcmpMsgInType5 uint64 `json:"icmpmsg_in_type5"` + IcmpMsgInType8 uint64 `json:"icmpmsg_in_type8"` + IcmpMsgInType11 uint64 `json:"icmpmsg_in_type11"` + IcmpMsgInType13 uint64 `json:"icmpmsg_in_type13"` + IcmpMsgOutType0 uint64 `json:"icmpmsg_out_type0"` + IcmpMsgOutType3 uint64 `json:"icmpmsg_out_type3"` + IcmpMsgOutType8 uint64 `json:"icmpmsg_out_type8"` + IcmpMsgOutType14 uint64 `json:"icmpmsg_out_type14"` + IcmpMsgOutType69 uint64 `json:"icmpmsg_out_type69"` + // TCP + TcpRtoAlgorithm uint64 `json:"tcp_rto_algorithm"` + TcpRtoMin uint64 `json:"tcp_rto_min"` + TcpRtoMax uint64 `json:"tcp_rto_max"` + TcpMaxConn uint64 `json:"tcp_max_conn"` + TcpActiveOpens uint64 `json:"tcp_active_opens"` + TcpPassiveOpens uint64 `json:"tcp_passive_opens"` + TcpAttemptFails uint64 `json:"tcp_attempt_fails"` + TcpEstabResets uint64 `json:"tcp_estab_resets"` + TcpCurrEstab uint64 `json:"tcp_curr_estab"` + TcpInSegs uint64 `json:"tcp_in_segs"` + TcpOutSegs uint64 `json:"tcp_out_segs"` + TcpRetransSegs uint64 `json:"tcp_retrans_segs"` + TcpInErrs uint64 `json:"tcp_in_errs"` + TcpOutRsts uint64 `json:"tcp_out_rsts"` + TcpInCsumErrors uint64 `json:"tcp_in_csum_errors"` + // UDP + UdpInDatagrams uint64 `json:"udp_in_datagrams"` + UdpNoPorts uint64 `json:"udp_no_ports"` + UdpInErrors uint64 `json:"udp_in_errors"` + UdpOutDatagrams uint64 `json:"udp_out_datagrams"` + UdpRcvbufErrors uint64 `json:"udp_rcvbuf_errors"` + UdpSndbufErrors uint64 `json:"udp_sndbuf_errors"` + UdpInCsumErrors uint64 `json:"udp_in_csum_errors"` + // UDPLite + UdpLiteInDatagrams uint64 `json:"udp_lite_in_datagrams"` + UdpLiteNoPorts uint64 `json:"udp_lite_no_ports"` + UdpLiteInErrors uint64 `json:"udp_lite_in_errors"` + UdpLiteOutDatagrams uint64 `json:"udp_lite_out_datagrams"` + UdpLiteRcvbufErrors uint64 `json:"udp_lite_rcvbuf_errors"` + UdpLiteSndbufErrors uint64 `json:"udp_lite_sndbuf_errors"` + UdpLiteInCsumErrors uint64 `json:"udp_lite_in_csum_errors"` +} + +func ReadSnmp(path string) (*Snmp, error) { + data, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(data), "\n") + + // Maps an SNMP metric to its value (i.e. SyncookiesSent --> 0) + statMap := make(map[string]string) + + // patterns + // Ip: Forwarding DefaultTTL InReceives InHdrErrors... <-- header + // Ip: 2 64 9305753793 0 0 0 0 0... <-- values + + for i := 1; i < len(lines); i = i + 2 { + headers := strings.Fields(lines[i-1][strings.Index(lines[i-1], ":")+1:]) + values := strings.Fields(lines[i][strings.Index(lines[i], ":")+1:]) + protocol := strings.Replace(strings.Fields(lines[i-1])[0], ":", "", -1) + + for j, header := range headers { + var val string + if len(values) > j { + val = values[j] + } else { + val = "UNKNOWN" + } + + statMap[protocol+header] = val + } + } + + var snmp Snmp = Snmp{} + + elem := reflect.ValueOf(&snmp).Elem() + typeOfElem := elem.Type() + + for i := 0; i < elem.NumField(); i++ { + if val, ok := statMap[typeOfElem.Field(i).Name]; ok { + parsedVal, _ := strconv.ParseUint(val, 10, 64) + elem.Field(i).SetUint(parsedVal) + } + } + + return &snmp, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/sockstat.go b/vendor/github.com/c9s/goprocinfo/linux/sockstat.go new file mode 100644 index 00000000..bf79652a --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/sockstat.go @@ -0,0 +1,98 @@ +package linux + +import ( + "io/ioutil" + "reflect" + "strconv" + "strings" +) + +type SockStat struct { + // sockets: + SocketsUsed uint64 `json:"sockets_used" field:"sockets.used"` + + // TCP: + TCPInUse uint64 `json:"tcp_in_use" field:"TCP.inuse"` + TCPOrphan uint64 `json:"tcp_orphan" field:"TCP.orphan"` + TCPTimeWait uint64 `json:"tcp_time_wait" field:"TCP.tw"` + TCPAllocated uint64 `json:"tcp_allocated" field:"TCP.alloc"` + TCPMemory uint64 `json:"tcp_memory" field:"TCP.mem"` + + //TCP6: + TCP6InUse uint64 `json:"tcp6_in_use" field:"TCP6.inuse"` + + // UDP: + UDPInUse uint64 `json:"udp_in_use" field:"UDP.inuse"` + UDPMemory uint64 `json:"udp_memory" field:"UDP.mem"` + + // UDP6: + UDP6InUse uint64 `json:"udp6_in_use" field:"UDP6.inuse"` + + // UDPLITE: + UDPLITEInUse uint64 `json:"udplite_in_use" field:"UDPLITE.inuse"` + + // UDPLITE6: + UDPLITE6InUse uint64 `json:"udplite6_in_use" field:"UDPLITE6.inuse"` + + // RAW: + RAWInUse uint64 `json:"raw_in_use" field:"RAW.inuse"` + + // RAW6: + RAW6InUse uint64 `json:"raw6_in_use" field:"RAW6.inuse"` + + // FRAG: + FRAGInUse uint64 `json:"frag_in_use" field:"FRAG.inuse"` + FRAGMemory uint64 `json:"frag_memory" field:"FRAG.memory"` + + // FRAG6: + FRAG6InUse uint64 `json:"frag6_in_use" field:"FRAG6.inuse"` + FRAG6Memory uint64 `json:"frag6_memory" field:"FRAG6.memory"` +} + +func ReadSockStat(path string) (*SockStat, error) { + data, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(data), "\n") + + // Maps a meminfo metric to its value (i.e. MemTotal --> 100000) + statMap := map[string]uint64{} + + var sockStat SockStat = SockStat{} + + for _, line := range lines { + if strings.Index(line, ":") == -1 { + continue + } + + statType := line[0:strings.Index(line, ":")] + "." + + // The fields have this pattern: inuse 27 orphan 1 tw 23 alloc 31 mem 3 + // The stats are grouped into pairs and need to be parsed and placed into the stat map. + key := "" + for k, v := range strings.Fields(line[strings.Index(line, ":")+1:]) { + // Every second field is a value. + if (k+1)%2 != 0 { + key = v + continue + } + val, _ := strconv.ParseUint(v, 10, 64) + statMap[statType+key] = val + } + } + + elem := reflect.ValueOf(&sockStat).Elem() + typeOfElem := elem.Type() + + for i := 0; i < elem.NumField(); i++ { + val, ok := statMap[typeOfElem.Field(i).Tag.Get("field")] + if ok { + elem.Field(i).SetUint(val) + } + } + + return &sockStat, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/stat.go b/vendor/github.com/c9s/goprocinfo/linux/stat.go new file mode 100644 index 00000000..8c921c38 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/stat.go @@ -0,0 +1,106 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" + "time" +) + +type Stat struct { + CPUStatAll CPUStat `json:"cpu_all"` + CPUStats []CPUStat `json:"cpus"` + Interrupts uint64 `json:"intr"` + ContextSwitches uint64 `json:"ctxt"` + BootTime time.Time `json:"btime"` + Processes uint64 `json:"processes"` + ProcsRunning uint64 `json:"procs_running"` + ProcsBlocked uint64 `json:"procs_blocked"` +} + +type CPUStat struct { + Id string `json:"id"` + User uint64 `json:"user"` + Nice uint64 `json:"nice"` + System uint64 `json:"system"` + Idle uint64 `json:"idle"` + IOWait uint64 `json:"iowait"` + IRQ uint64 `json:"irq"` + SoftIRQ uint64 `json:"softirq"` + Steal uint64 `json:"steal"` + Guest uint64 `json:"guest"` + GuestNice uint64 `json:"guest_nice"` +} + +func createCPUStat(fields []string) *CPUStat { + s := CPUStat{} + s.Id = fields[0] + + for i := 1; i < len(fields); i++ { + v, _ := strconv.ParseUint(fields[i], 10, 64) + switch i { + case 1: + s.User = v + case 2: + s.Nice = v + case 3: + s.System = v + case 4: + s.Idle = v + case 5: + s.IOWait = v + case 6: + s.IRQ = v + case 7: + s.SoftIRQ = v + case 8: + s.Steal = v + case 9: + s.Guest = v + case 10: + s.GuestNice = v + } + } + return &s +} + +func ReadStat(path string) (*Stat, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + content := string(b) + lines := strings.Split(content, "\n") + + var stat Stat = Stat{} + + for i, line := range lines { + fields := strings.Fields(line) + if len(fields) == 0 { + continue + } + if fields[0][:3] == "cpu" { + if cpuStat := createCPUStat(fields); cpuStat != nil { + if i == 0 { + stat.CPUStatAll = *cpuStat + } else { + stat.CPUStats = append(stat.CPUStats, *cpuStat) + } + } + } else if fields[0] == "intr" { + stat.Interrupts, _ = strconv.ParseUint(fields[1], 10, 64) + } else if fields[0] == "ctxt" { + stat.ContextSwitches, _ = strconv.ParseUint(fields[1], 10, 64) + } else if fields[0] == "btime" { + seconds, _ := strconv.ParseInt(fields[1], 10, 64) + stat.BootTime = time.Unix(seconds, 0) + } else if fields[0] == "processes" { + stat.Processes, _ = strconv.ParseUint(fields[1], 10, 64) + } else if fields[0] == "procs_running" { + stat.ProcsRunning, _ = strconv.ParseUint(fields[1], 10, 64) + } else if fields[0] == "procs_blocked" { + stat.ProcsBlocked, _ = strconv.ParseUint(fields[1], 10, 64) + } + } + return &stat, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/uptime.go b/vendor/github.com/c9s/goprocinfo/linux/uptime.go new file mode 100644 index 00000000..393076c4 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/uptime.go @@ -0,0 +1,43 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" + "time" +) + +type Uptime struct { + Total float64 `json:"total"` + Idle float64 `json:"idle"` +} + +func (self *Uptime) GetTotalDuration() time.Duration { + return time.Duration(self.Total) * time.Second +} + +func (self *Uptime) GetIdleDuration() time.Duration { + return time.Duration(self.Idle) * time.Second +} + +func (self *Uptime) CalculateIdle() float64 { + // XXX + // num2/(num1*N) # N = SMP CPU numbers + return 0 +} + +func ReadUptime(path string) (*Uptime, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + fields := strings.Fields(string(b)) + uptime := Uptime{} + if uptime.Total, err = strconv.ParseFloat(fields[0], 64); err != nil { + return nil, err + } + if uptime.Idle, err = strconv.ParseFloat(fields[1], 64); err != nil { + return nil, err + } + return &uptime, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/vmstat.go b/vendor/github.com/c9s/goprocinfo/linux/vmstat.go new file mode 100644 index 00000000..91d5a716 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/vmstat.go @@ -0,0 +1,373 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +type VMStat struct { + NrFreePages uint64 `json:"nr_free_pages"` + NrAllocBatch uint64 `json:"nr_alloc_batch"` + NrInactiveAnon uint64 `json:"nr_inactive_anon"` + NrActiveAnon uint64 `json:"nr_active_anon"` + NrInactiveFile uint64 `json:"nr_inactive_file"` + NrActiveFile uint64 `json:"nr_active_file"` + NrUnevictable uint64 `json:"nr_unevictable"` + NrMlock uint64 `json:"nr_mlock"` + NrAnonPages uint64 `json:"nr_anon_pages"` + NrMapped uint64 `json:"nr_mapped"` + NrFilePages uint64 `json:"nr_file_pages"` + NrDirty uint64 `json:"nr_dirty"` + NrWriteback uint64 `json:"nr_writeback"` + NrSlabReclaimable uint64 `json:"nr_slab_reclaimable"` + NrSlabUnreclaimable uint64 `json:"nr_slab_unreclaimable"` + NrPageTablePages uint64 `json:"nr_page_table_pages"` + NrKernelStack uint64 `json:"nr_kernel_stack"` + NrUnstable uint64 `json:"nr_unstable"` + NrBounce uint64 `json:"nr_bounce"` + NrVmscanWrite uint64 `json:"nr_vmscan_write"` + NrVmscanImmediateReclaim uint64 `json:"nr_vmscan_immediate_reclaim"` + NrWritebackTemp uint64 `json:"nr_writeback_temp"` + NrIsolatedAnon uint64 `json:"nr_isolated_anon"` + NrIsolatedFile uint64 `json:"nr_isolated_file"` + NrShmem uint64 `json:"nr_shmem"` + NrDirtied uint64 `json:"nr_dirtied"` + NrWritten uint64 `json:"nr_written"` + NumaHit uint64 `json:"numa_hit"` + NumaMiss uint64 `json:"numa_miss"` + NumaForeign uint64 `json:"numa_foreign"` + NumaInterleave uint64 `json:"numa_interleave"` + NumaLocal uint64 `json:"numa_local"` + NumaOther uint64 `json:"numa_other"` + WorkingsetRefault uint64 `json:"workingset_refault"` + WorkingsetActivate uint64 `json:"workingset_activate"` + WorkingsetNodereclaim uint64 `json:"workingset_nodereclaim"` + NrAnonTransparentHugepages uint64 `json:"nr_anon_transparent_hugepages"` + NrFreeCma uint64 `json:"nr_free_cma"` + NrDirtyThreshold uint64 `json:"nr_dirty_threshold"` + NrDirtyBackgroundThreshold uint64 `json:"nr_dirty_background_threshold"` + PagePagein uint64 `json:"pgpgin"` + PagePageout uint64 `json:"pgpgout"` + PageSwapin uint64 `json:"pswpin"` + PageSwapout uint64 `json:"pswpout"` + PageAllocDMA uint64 `json:"pgalloc_dma"` + PageAllocDMA32 uint64 `json:"pgalloc_dma32"` + PageAllocNormal uint64 `json:"pgalloc_normal"` + PageAllocMovable uint64 `json:"pgalloc_movable"` + PageFree uint64 `json:"pgfree"` + PageActivate uint64 `json:"pgactivate"` + PageDeactivate uint64 `json:"pgdeactivate"` + PageFault uint64 `json:"pgfault"` + PageMajorFault uint64 `json:"pgmajfault"` + PageRefillDMA uint64 `json:"pgrefill_dma"` + PageRefillDMA32 uint64 `json:"pgrefill_dma32"` + PageRefillMormal uint64 `json:"pgrefill_normal"` + PageRefillMovable uint64 `json:"pgrefill_movable"` + PageStealKswapdDMA uint64 `json:"pgsteal_kswapd_dma"` + PageStealKswapdDMA32 uint64 `json:"pgsteal_kswapd_dma32"` + PageStealKswapdNormal uint64 `json:"pgsteal_kswapd_normal"` + PageStealKswapdMovable uint64 `json:"pgsteal_kswapd_movable"` + PageStealDirectDMA uint64 `json:"pgsteal_direct_dma"` + PageStealDirectDMA32 uint64 `json:"pgsteal_direct_dma32"` + PageStealDirectNormal uint64 `json:"pgsteal_direct_normal"` + PageStealDirectMovable uint64 `json:"pgsteal_direct_movable"` + PageScanKswapdDMA uint64 `json:"pgscan_kswapd_dma"` + PageScanKswapdDMA32 uint64 `json:"pgscan_kswapd_dma32"` + PageScanKswapdNormal uint64 `json:"pgscan_kswapd_normal"` + PageScanKswapdMovable uint64 `json:"pgscan_kswapd_movable"` + PageScanDirectDMA uint64 `json:"pgscan_direct_dma"` + PageScanDirectDMA32 uint64 `json:"pgscan_direct_dma32"` + PageScanDirectNormal uint64 `json:"pgscan_direct_normal"` + PageScanDirectMovable uint64 `json:"pgscan_direct_movable"` + PageScanDirectThrottle uint64 `json:"pgscan_direct_throttle"` + ZoneReclaimFailed uint64 `json:"zone_reclaim_failed"` + PageInodeSteal uint64 `json:"pginodesteal"` + SlabsScanned uint64 `json:"slabs_scanned"` + KswapdInodesteal uint64 `json:"kswapd_inodesteal"` + KswapdLowWatermarkHitQuickly uint64 `json:"kswapd_low_wmark_hit_quickly"` + KswapdHighWatermarkHitQuickly uint64 `json:"kswapd_high_wmark_hit_quickly"` + PageoutRun uint64 `json:"pageoutrun"` + AllocStall uint64 `json:"allocstall"` + PageRotated uint64 `json:"pgrotated"` + DropPagecache uint64 `json:"drop_pagecache"` + DropSlab uint64 `json:"drop_slab"` + NumaPteUpdates uint64 `json:"numa_pte_updates"` + NumaHugePteUpdates uint64 `json:"numa_huge_pte_updates"` + NumaHintFaults uint64 `json:"numa_hint_faults"` + NumaHintFaults_local uint64 `json:"numa_hint_faults_local"` + NumaPagesMigrated uint64 `json:"numa_pages_migrated"` + PageMigrateSuccess uint64 `json:"pgmigrate_success"` + PageMigrateFail uint64 `json:"pgmigrate_fail"` + CompactMigrateScanned uint64 `json:"compact_migrate_scanned"` + CompactFreeScanned uint64 `json:"compact_free_scanned"` + CompactIsolated uint64 `json:"compact_isolated"` + CompactStall uint64 `json:"compact_stall"` + CompactFail uint64 `json:"compact_fail"` + CompactSuccess uint64 `json:"compact_success"` + HtlbBuddyAllocSuccess uint64 `json:"htlb_buddy_alloc_success"` + HtlbBuddyAllocFail uint64 `json:"htlb_buddy_alloc_fail"` + UnevictablePagesCulled uint64 `json:"unevictable_pgs_culled"` + UnevictablePagesScanned uint64 `json:"unevictable_pgs_scanned"` + UnevictablePagesRescued uint64 `json:"unevictable_pgs_rescued"` + UnevictablePagesMlocked uint64 `json:"unevictable_pgs_mlocked"` + UnevictablePagesMunlocked uint64 `json:"unevictable_pgs_munlocked"` + UnevictablePagesCleared uint64 `json:"unevictable_pgs_cleared"` + UnevictablePagesStranded uint64 `json:"unevictable_pgs_stranded"` + THPFaultAlloc uint64 `json:"thp_fault_alloc"` + THPFaultFallback uint64 `json:"thp_fault_fallback"` + THPCollapseAlloc uint64 `json:"thp_collapse_alloc"` + THPCollapseAllocFailed uint64 `json:"thp_collapse_alloc_failed"` + THPSplit uint64 `json:"thp_split"` + THPZeroPageAlloc uint64 `json:"thp_zero_page_alloc"` + THPZeroPageAllocFailed uint64 `json:"thp_zero_page_alloc_failed"` +} + +func ReadVMStat(path string) (*VMStat, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + content := string(b) + lines := strings.Split(content, "\n") + vmstat := VMStat{} + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) != 2 { + continue + } + name := fields[0] + value, _ := strconv.ParseUint(fields[1], 10, 64) + switch name { + case "nr_free_pages": + vmstat.NrFreePages = value + case "nr_alloc_batch": + vmstat.NrAllocBatch = value + case "nr_inactive_anon": + vmstat.NrInactiveAnon = value + case "nr_active_anon": + vmstat.NrActiveAnon = value + case "nr_inactive_file": + vmstat.NrInactiveFile = value + case "nr_active_file": + vmstat.NrActiveFile = value + case "nr_unevictable": + vmstat.NrUnevictable = value + case "nr_mlock": + vmstat.NrMlock = value + case "nr_anon_pages": + vmstat.NrAnonPages = value + case "nr_mapped": + vmstat.NrMapped = value + case "nr_file_pages": + vmstat.NrFilePages = value + case "nr_dirty": + vmstat.NrDirty = value + case "nr_writeback": + vmstat.NrWriteback = value + case "nr_slab_reclaimable": + vmstat.NrSlabReclaimable = value + case "nr_slab_unreclaimable": + vmstat.NrSlabUnreclaimable = value + case "nr_page_table_pages": + vmstat.NrPageTablePages = value + case "nr_kernel_stack": + vmstat.NrKernelStack = value + case "nr_unstable": + vmstat.NrUnstable = value + case "nr_bounce": + vmstat.NrBounce = value + case "nr_vmscan_write": + vmstat.NrVmscanWrite = value + case "nr_vmscan_immediate_reclaim": + vmstat.NrVmscanImmediateReclaim = value + case "nr_writeback_temp": + vmstat.NrWritebackTemp = value + case "nr_isolated_anon": + vmstat.NrIsolatedAnon = value + case "nr_isolated_file": + vmstat.NrIsolatedFile = value + case "nr_shmem": + vmstat.NrShmem = value + case "nr_dirtied": + vmstat.NrDirtied = value + case "nr_written": + vmstat.NrWritten = value + case "numa_hit": + vmstat.NumaHit = value + case "numa_miss": + vmstat.NumaMiss = value + case "numa_foreign": + vmstat.NumaForeign = value + case "numa_interleave": + vmstat.NumaInterleave = value + case "numa_local": + vmstat.NumaLocal = value + case "numa_other": + vmstat.NumaOther = value + case "workingset_refault": + vmstat.WorkingsetRefault = value + case "workingset_activate": + vmstat.WorkingsetActivate = value + case "workingset_nodereclaim": + vmstat.WorkingsetNodereclaim = value + case "nr_anon_transparent_hugepages": + vmstat.NrAnonTransparentHugepages = value + case "nr_free_cma": + vmstat.NrFreeCma = value + case "nr_dirty_threshold": + vmstat.NrDirtyThreshold = value + case "nr_dirty_background_threshold": + vmstat.NrDirtyBackgroundThreshold = value + case "pgpgin": + vmstat.PagePagein = value + case "pgpgout": + vmstat.PagePageout = value + case "pswpin": + vmstat.PageSwapin = value + case "pswpout": + vmstat.PageSwapout = value + case "pgalloc_dma": + vmstat.PageAllocDMA = value + case "pgalloc_dma32": + vmstat.PageAllocDMA32 = value + case "pgalloc_normal": + vmstat.PageAllocNormal = value + case "pgalloc_movable": + vmstat.PageAllocMovable = value + case "pgfree": + vmstat.PageFree = value + case "pgactivate": + vmstat.PageActivate = value + case "pgdeactivate": + vmstat.PageDeactivate = value + case "pgfault": + vmstat.PageFault = value + case "pgmajfault": + vmstat.PageMajorFault = value + case "pgrefill_dma": + vmstat.PageRefillDMA = value + case "pgrefill_dma32": + vmstat.PageRefillDMA32 = value + case "pgrefill_normal": + vmstat.PageRefillMormal = value + case "pgrefill_movable": + vmstat.PageRefillMovable = value + case "pgsteal_kswapd_dma": + vmstat.PageStealKswapdDMA = value + case "pgsteal_kswapd_dma32": + vmstat.PageStealKswapdDMA32 = value + case "pgsteal_kswapd_normal": + vmstat.PageStealKswapdNormal = value + case "pgsteal_kswapd_movable": + vmstat.PageStealKswapdMovable = value + case "pgsteal_direct_dma": + vmstat.PageStealDirectDMA = value + case "pgsteal_direct_dma32": + vmstat.PageStealDirectDMA32 = value + case "pgsteal_direct_normal": + vmstat.PageStealDirectNormal = value + case "pgsteal_direct_movable": + vmstat.PageStealDirectMovable = value + case "pgscan_kswapd_dma": + vmstat.PageScanKswapdDMA = value + case "pgscan_kswapd_dma32": + vmstat.PageScanKswapdDMA32 = value + case "pgscan_kswapd_normal": + vmstat.PageScanKswapdNormal = value + case "pgscan_kswapd_movable": + vmstat.PageScanKswapdMovable = value + case "pgscan_direct_dma": + vmstat.PageScanDirectDMA = value + case "pgscan_direct_dma32": + vmstat.PageScanDirectDMA32 = value + case "pgscan_direct_normal": + vmstat.PageScanDirectNormal = value + case "pgscan_direct_movable": + vmstat.PageScanDirectMovable = value + case "pgscan_direct_throttle": + vmstat.PageScanDirectThrottle = value + case "zone_reclaim_failed": + vmstat.ZoneReclaimFailed = value + case "pginodesteal": + vmstat.PageInodeSteal = value + case "slabs_scanned": + vmstat.SlabsScanned = value + case "kswapd_inodesteal": + vmstat.KswapdInodesteal = value + case "kswapd_low_wmark_hit_quickly": + vmstat.KswapdLowWatermarkHitQuickly = value + case "kswapd_high_wmark_hit_quickly": + vmstat.KswapdHighWatermarkHitQuickly = value + case "pageoutrun": + vmstat.PageoutRun = value + case "allocstall": + vmstat.AllocStall = value + case "pgrotated": + vmstat.PageRotated = value + case "drop_pagecache": + vmstat.DropPagecache = value + case "drop_slab": + vmstat.DropSlab = value + case "numa_pte_updates": + vmstat.NumaPteUpdates = value + case "numa_huge_pte_updates": + vmstat.NumaHugePteUpdates = value + case "numa_hint_faults": + vmstat.NumaHintFaults = value + case "numa_hint_faults_local": + vmstat.NumaHintFaults_local = value + case "numa_pages_migrated": + vmstat.NumaPagesMigrated = value + case "pgmigrate_success": + vmstat.PageMigrateSuccess = value + case "pgmigrate_fail": + vmstat.PageMigrateFail = value + case "compact_migrate_scanned": + vmstat.CompactMigrateScanned = value + case "compact_free_scanned": + vmstat.CompactFreeScanned = value + case "compact_isolated": + vmstat.CompactIsolated = value + case "compact_stall": + vmstat.CompactStall = value + case "compact_fail": + vmstat.CompactFail = value + case "compact_success": + vmstat.CompactSuccess = value + case "htlb_buddy_alloc_success": + vmstat.HtlbBuddyAllocSuccess = value + case "htlb_buddy_alloc_fail": + vmstat.HtlbBuddyAllocFail = value + case "unevictable_pgs_culled": + vmstat.UnevictablePagesCulled = value + case "unevictable_pgs_scanned": + vmstat.UnevictablePagesScanned = value + case "unevictable_pgs_rescued": + vmstat.UnevictablePagesRescued = value + case "unevictable_pgs_mlocked": + vmstat.UnevictablePagesMlocked = value + case "unevictable_pgs_munlocked": + vmstat.UnevictablePagesMunlocked = value + case "unevictable_pgs_cleared": + vmstat.UnevictablePagesCleared = value + case "unevictable_pgs_stranded": + vmstat.UnevictablePagesStranded = value + case "thp_fault_alloc": + vmstat.THPFaultAlloc = value + case "thp_fault_fallback": + vmstat.THPFaultFallback = value + case "thp_collapse_alloc": + vmstat.THPCollapseAlloc = value + case "thp_collapse_alloc_failed": + vmstat.THPCollapseAllocFailed = value + case "thp_split": + vmstat.THPSplit = value + case "thp_zero_page_alloc": + vmstat.THPZeroPageAlloc = value + case "thp_zero_page_alloc_failed": + vmstat.THPZeroPageAllocFailed = value + } + } + return &vmstat, nil +} diff --git a/vendor/github.com/go-logr/logr/README.md b/vendor/github.com/go-logr/logr/README.md index a8c29bfb..8969526a 100644 --- a/vendor/github.com/go-logr/logr/README.md +++ b/vendor/github.com/go-logr/logr/README.md @@ -91,11 +91,12 @@ logr design but also left out some parts and changed others: | Adding a name to a logger | `WithName` | no API | | Modify verbosity of log entries in a call chain | `V` | no API | | Grouping of key/value pairs | not supported | `WithGroup`, `GroupValue` | +| Pass context for extracting additional values | no API | API variants like `InfoCtx` | The high-level slog API is explicitly meant to be one of many different APIs that can be layered on top of a shared `slog.Handler`. logr is one such -alternative API, with [interoperability](#slog-interoperability) provided by the [`slogr`](slogr) -package. +alternative API, with [interoperability](#slog-interoperability) provided by +some conversion functions. ### Inspiration @@ -145,24 +146,24 @@ There are implementations for the following logging libraries: ## slog interoperability Interoperability goes both ways, using the `logr.Logger` API with a `slog.Handler` -and using the `slog.Logger` API with a `logr.LogSink`. [slogr](./slogr) provides `NewLogr` and -`NewSlogHandler` API calls to convert between a `logr.Logger` and a `slog.Handler`. +and using the `slog.Logger` API with a `logr.LogSink`. `FromSlogHandler` and +`ToSlogHandler` convert between a `logr.Logger` and a `slog.Handler`. As usual, `slog.New` can be used to wrap such a `slog.Handler` in the high-level -slog API. `slogr` itself leaves that to the caller. +slog API. -## Using a `logr.Sink` as backend for slog +### Using a `logr.LogSink` as backend for slog Ideally, a logr sink implementation should support both logr and slog by -implementing both the normal logr interface(s) and `slogr.SlogSink`. Because +implementing both the normal logr interface(s) and `SlogSink`. Because of a conflict in the parameters of the common `Enabled` method, it is [not possible to implement both slog.Handler and logr.Sink in the same type](https://github.com/golang/go/issues/59110). If both are supported, log calls can go from the high-level APIs to the backend -without the need to convert parameters. `NewLogr` and `NewSlogHandler` can +without the need to convert parameters. `FromSlogHandler` and `ToSlogHandler` can convert back and forth without adding additional wrappers, with one exception: when `Logger.V` was used to adjust the verbosity for a `slog.Handler`, then -`NewSlogHandler` has to use a wrapper which adjusts the verbosity for future +`ToSlogHandler` has to use a wrapper which adjusts the verbosity for future log calls. Such an implementation should also support values that implement specific @@ -187,13 +188,13 @@ Not supporting slog has several drawbacks: These drawbacks are severe enough that applications using a mixture of slog and logr should switch to a different backend. -## Using a `slog.Handler` as backend for logr +### Using a `slog.Handler` as backend for logr Using a plain `slog.Handler` without support for logr works better than the other direction: - All logr verbosity levels can be mapped 1:1 to their corresponding slog level by negating them. -- Stack unwinding is done by the `slogr.SlogSink` and the resulting program +- Stack unwinding is done by the `SlogSink` and the resulting program counter is passed to the `slog.Handler`. - Names added via `Logger.WithName` are gathered and recorded in an additional attribute with `logger` as key and the names separated by slash as value. @@ -205,27 +206,39 @@ ideally support both `logr.Marshaler` and `slog.Valuer`. If compatibility with logr implementations without slog support is not important, then `slog.Valuer` is sufficient. -## Context support for slog +### Context support for slog Storing a logger in a `context.Context` is not supported by -slog. `logr.NewContext` and `logr.FromContext` can be used with slog like this -to fill this gap: - - func HandlerFromContext(ctx context.Context) slog.Handler { - logger, err := logr.FromContext(ctx) - if err == nil { - return slogr.NewSlogHandler(logger) - } - return slog.Default().Handler() - } - - func ContextWithHandler(ctx context.Context, handler slog.Handler) context.Context { - return logr.NewContext(ctx, slogr.NewLogr(handler)) - } - -The downside is that storing and retrieving a `slog.Handler` needs more -allocations compared to using a `logr.Logger`. Therefore the recommendation is -to use the `logr.Logger` API in code which uses contextual logging. +slog. `NewContextWithSlogLogger` and `FromContextAsSlogLogger` can be +used to fill this gap. They store and retrieve a `slog.Logger` pointer +under the same context key that is also used by `NewContext` and +`FromContext` for `logr.Logger` value. + +When `NewContextWithSlogLogger` is followed by `FromContext`, the latter will +automatically convert the `slog.Logger` to a +`logr.Logger`. `FromContextAsSlogLogger` does the same for the other direction. + +With this approach, binaries which use either slog or logr are as efficient as +possible with no unnecessary allocations. This is also why the API stores a +`slog.Logger` pointer: when storing a `slog.Handler`, creating a `slog.Logger` +on retrieval would need to allocate one. + +The downside is that switching back and forth needs more allocations. Because +logr is the API that is already in use by different packages, in particular +Kubernetes, the recommendation is to use the `logr.Logger` API in code which +uses contextual logging. + +An alternative to adding values to a logger and storing that logger in the +context is to store the values in the context and to configure a logging +backend to extract those values when emitting log entries. This only works when +log calls are passed the context, which is not supported by the logr API. + +With the slog API, it is possible, but not +required. https://github.com/veqryn/slog-context is a package for slog which +provides additional support code for this approach. It also contains wrappers +for the context functions in logr, so developers who prefer to not use the logr +APIs directly can use those instead and the resulting code will still be +interoperable with logr. ## FAQ diff --git a/vendor/github.com/go-logr/logr/context.go b/vendor/github.com/go-logr/logr/context.go new file mode 100644 index 00000000..de8bcc3a --- /dev/null +++ b/vendor/github.com/go-logr/logr/context.go @@ -0,0 +1,33 @@ +/* +Copyright 2023 The logr Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logr + +// contextKey is how we find Loggers in a context.Context. With Go < 1.21, +// the value is always a Logger value. With Go >= 1.21, the value can be a +// Logger value or a slog.Logger pointer. +type contextKey struct{} + +// notFoundError exists to carry an IsNotFound method. +type notFoundError struct{} + +func (notFoundError) Error() string { + return "no logr.Logger was present" +} + +func (notFoundError) IsNotFound() bool { + return true +} diff --git a/vendor/github.com/go-logr/logr/context_noslog.go b/vendor/github.com/go-logr/logr/context_noslog.go new file mode 100644 index 00000000..f012f9a1 --- /dev/null +++ b/vendor/github.com/go-logr/logr/context_noslog.go @@ -0,0 +1,49 @@ +//go:build !go1.21 +// +build !go1.21 + +/* +Copyright 2019 The logr Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logr + +import ( + "context" +) + +// FromContext returns a Logger from ctx or an error if no Logger is found. +func FromContext(ctx context.Context) (Logger, error) { + if v, ok := ctx.Value(contextKey{}).(Logger); ok { + return v, nil + } + + return Logger{}, notFoundError{} +} + +// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this +// returns a Logger that discards all log messages. +func FromContextOrDiscard(ctx context.Context) Logger { + if v, ok := ctx.Value(contextKey{}).(Logger); ok { + return v + } + + return Discard() +} + +// NewContext returns a new Context, derived from ctx, which carries the +// provided Logger. +func NewContext(ctx context.Context, logger Logger) context.Context { + return context.WithValue(ctx, contextKey{}, logger) +} diff --git a/vendor/github.com/go-logr/logr/context_slog.go b/vendor/github.com/go-logr/logr/context_slog.go new file mode 100644 index 00000000..065ef0b8 --- /dev/null +++ b/vendor/github.com/go-logr/logr/context_slog.go @@ -0,0 +1,83 @@ +//go:build go1.21 +// +build go1.21 + +/* +Copyright 2019 The logr Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logr + +import ( + "context" + "fmt" + "log/slog" +) + +// FromContext returns a Logger from ctx or an error if no Logger is found. +func FromContext(ctx context.Context) (Logger, error) { + v := ctx.Value(contextKey{}) + if v == nil { + return Logger{}, notFoundError{} + } + + switch v := v.(type) { + case Logger: + return v, nil + case *slog.Logger: + return FromSlogHandler(v.Handler()), nil + default: + // Not reached. + panic(fmt.Sprintf("unexpected value type for logr context key: %T", v)) + } +} + +// FromContextAsSlogLogger returns a slog.Logger from ctx or nil if no such Logger is found. +func FromContextAsSlogLogger(ctx context.Context) *slog.Logger { + v := ctx.Value(contextKey{}) + if v == nil { + return nil + } + + switch v := v.(type) { + case Logger: + return slog.New(ToSlogHandler(v)) + case *slog.Logger: + return v + default: + // Not reached. + panic(fmt.Sprintf("unexpected value type for logr context key: %T", v)) + } +} + +// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this +// returns a Logger that discards all log messages. +func FromContextOrDiscard(ctx context.Context) Logger { + if logger, err := FromContext(ctx); err == nil { + return logger + } + return Discard() +} + +// NewContext returns a new Context, derived from ctx, which carries the +// provided Logger. +func NewContext(ctx context.Context, logger Logger) context.Context { + return context.WithValue(ctx, contextKey{}, logger) +} + +// NewContextWithSlogLogger returns a new Context, derived from ctx, which carries the +// provided slog.Logger. +func NewContextWithSlogLogger(ctx context.Context, logger *slog.Logger) context.Context { + return context.WithValue(ctx, contextKey{}, logger) +} diff --git a/vendor/github.com/go-logr/logr/logr.go b/vendor/github.com/go-logr/logr/logr.go index 2a5075a1..b4428e10 100644 --- a/vendor/github.com/go-logr/logr/logr.go +++ b/vendor/github.com/go-logr/logr/logr.go @@ -207,10 +207,6 @@ limitations under the License. // those. package logr -import ( - "context" -) - // New returns a new Logger instance. This is primarily used by libraries // implementing LogSink, rather than end users. Passing a nil sink will create // a Logger which discards all log lines. @@ -410,45 +406,6 @@ func (l Logger) IsZero() bool { return l.sink == nil } -// contextKey is how we find Loggers in a context.Context. -type contextKey struct{} - -// FromContext returns a Logger from ctx or an error if no Logger is found. -func FromContext(ctx context.Context) (Logger, error) { - if v, ok := ctx.Value(contextKey{}).(Logger); ok { - return v, nil - } - - return Logger{}, notFoundError{} -} - -// notFoundError exists to carry an IsNotFound method. -type notFoundError struct{} - -func (notFoundError) Error() string { - return "no logr.Logger was present" -} - -func (notFoundError) IsNotFound() bool { - return true -} - -// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this -// returns a Logger that discards all log messages. -func FromContextOrDiscard(ctx context.Context) Logger { - if v, ok := ctx.Value(contextKey{}).(Logger); ok { - return v - } - - return Discard() -} - -// NewContext returns a new Context, derived from ctx, which carries the -// provided Logger. -func NewContext(ctx context.Context, logger Logger) context.Context { - return context.WithValue(ctx, contextKey{}, logger) -} - // RuntimeInfo holds information that the logr "core" library knows which // LogSinks might want to know. type RuntimeInfo struct { diff --git a/vendor/github.com/go-logr/logr/slogr/sloghandler.go b/vendor/github.com/go-logr/logr/sloghandler.go similarity index 63% rename from vendor/github.com/go-logr/logr/slogr/sloghandler.go rename to vendor/github.com/go-logr/logr/sloghandler.go index ec6725ce..82d1ba49 100644 --- a/vendor/github.com/go-logr/logr/slogr/sloghandler.go +++ b/vendor/github.com/go-logr/logr/sloghandler.go @@ -17,18 +17,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -package slogr +package logr import ( "context" "log/slog" - - "github.com/go-logr/logr" ) type slogHandler struct { // May be nil, in which case all logs get discarded. - sink logr.LogSink + sink LogSink // Non-nil if sink is non-nil and implements SlogSink. slogSink SlogSink @@ -54,7 +52,7 @@ func (l *slogHandler) GetLevel() slog.Level { return l.levelBias } -func (l *slogHandler) Enabled(ctx context.Context, level slog.Level) bool { +func (l *slogHandler) Enabled(_ context.Context, level slog.Level) bool { return l.sink != nil && (level >= slog.LevelError || l.sink.Enabled(l.levelFromSlog(level))) } @@ -72,9 +70,7 @@ func (l *slogHandler) Handle(ctx context.Context, record slog.Record) error { kvList := make([]any, 0, 2*record.NumAttrs()) record.Attrs(func(attr slog.Attr) bool { - if attr.Key != "" { - kvList = append(kvList, l.addGroupPrefix(attr.Key), attr.Value.Resolve().Any()) - } + kvList = attrToKVs(attr, l.groupPrefix, kvList) return true }) if record.Level >= slog.LevelError { @@ -90,15 +86,15 @@ func (l *slogHandler) Handle(ctx context.Context, record slog.Record) error { // are called by Handle, code in slog gets skipped. // // This offset currently (Go 1.21.0) works for calls through -// slog.New(NewSlogHandler(...)). There's no guarantee that the call +// slog.New(ToSlogHandler(...)). There's no guarantee that the call // chain won't change. Wrapping the handler will also break unwinding. It's // still better than not adjusting at all.... // -// This cannot be done when constructing the handler because NewLogr needs +// This cannot be done when constructing the handler because FromSlogHandler needs // access to the original sink without this adjustment. A second copy would // work, but then WithAttrs would have to be called for both of them. -func (l *slogHandler) sinkWithCallDepth() logr.LogSink { - if sink, ok := l.sink.(logr.CallDepthLogSink); ok { +func (l *slogHandler) sinkWithCallDepth() LogSink { + if sink, ok := l.sink.(CallDepthLogSink); ok { return sink.WithCallDepth(2) } return l.sink @@ -109,60 +105,88 @@ func (l *slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return l } - copy := *l + clone := *l if l.slogSink != nil { - copy.slogSink = l.slogSink.WithAttrs(attrs) - copy.sink = copy.slogSink + clone.slogSink = l.slogSink.WithAttrs(attrs) + clone.sink = clone.slogSink } else { kvList := make([]any, 0, 2*len(attrs)) for _, attr := range attrs { - if attr.Key != "" { - kvList = append(kvList, l.addGroupPrefix(attr.Key), attr.Value.Resolve().Any()) - } + kvList = attrToKVs(attr, l.groupPrefix, kvList) } - copy.sink = l.sink.WithValues(kvList...) + clone.sink = l.sink.WithValues(kvList...) } - return © + return &clone } func (l *slogHandler) WithGroup(name string) slog.Handler { if l.sink == nil { return l } - copy := *l + if name == "" { + // slog says to inline empty groups + return l + } + clone := *l if l.slogSink != nil { - copy.slogSink = l.slogSink.WithGroup(name) - copy.sink = l.slogSink + clone.slogSink = l.slogSink.WithGroup(name) + clone.sink = clone.slogSink } else { - copy.groupPrefix = copy.addGroupPrefix(name) + clone.groupPrefix = addPrefix(clone.groupPrefix, name) + } + return &clone +} + +// attrToKVs appends a slog.Attr to a logr-style kvList. It handle slog Groups +// and other details of slog. +func attrToKVs(attr slog.Attr, groupPrefix string, kvList []any) []any { + attrVal := attr.Value.Resolve() + if attrVal.Kind() == slog.KindGroup { + groupVal := attrVal.Group() + grpKVs := make([]any, 0, 2*len(groupVal)) + prefix := groupPrefix + if attr.Key != "" { + prefix = addPrefix(groupPrefix, attr.Key) + } + for _, attr := range groupVal { + grpKVs = attrToKVs(attr, prefix, grpKVs) + } + kvList = append(kvList, grpKVs...) + } else if attr.Key != "" { + kvList = append(kvList, addPrefix(groupPrefix, attr.Key), attrVal.Any()) } - return © + + return kvList } -func (l *slogHandler) addGroupPrefix(name string) string { - if l.groupPrefix == "" { +func addPrefix(prefix, name string) string { + if prefix == "" { return name } - return l.groupPrefix + groupSeparator + name + if name == "" { + return prefix + } + return prefix + groupSeparator + name } // levelFromSlog adjusts the level by the logger's verbosity and negates it. // It ensures that the result is >= 0. This is necessary because the result is -// passed to a logr.LogSink and that API did not historically document whether +// passed to a LogSink and that API did not historically document whether // levels could be negative or what that meant. // // Some example usage: -// logrV0 := getMyLogger() -// logrV2 := logrV0.V(2) -// slogV2 := slog.New(slogr.NewSlogHandler(logrV2)) -// slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6) -// slogV2.Info("msg") // =~ logrV2.V(0) =~ logrV0.V(2) -// slogv2.Warn("msg") // =~ logrV2.V(-4) =~ logrV0.V(0) +// +// logrV0 := getMyLogger() +// logrV2 := logrV0.V(2) +// slogV2 := slog.New(logr.ToSlogHandler(logrV2)) +// slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6) +// slogV2.Info("msg") // =~ logrV2.V(0) =~ logrV0.V(2) +// slogv2.Warn("msg") // =~ logrV2.V(-4) =~ logrV0.V(0) func (l *slogHandler) levelFromSlog(level slog.Level) int { result := -level - result += l.levelBias // in case the original logr.Logger had a V level + result += l.levelBias // in case the original Logger had a V level if result < 0 { - result = 0 // because logr.LogSink doesn't expect negative V levels + result = 0 // because LogSink doesn't expect negative V levels } return int(result) } diff --git a/vendor/github.com/go-logr/logr/slogr/slogr.go b/vendor/github.com/go-logr/logr/slogr.go similarity index 66% rename from vendor/github.com/go-logr/logr/slogr/slogr.go rename to vendor/github.com/go-logr/logr/slogr.go index eb519ae2..28a83d02 100644 --- a/vendor/github.com/go-logr/logr/slogr/slogr.go +++ b/vendor/github.com/go-logr/logr/slogr.go @@ -17,54 +17,46 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package slogr enables usage of a slog.Handler with logr.Logger as front-end -// API and of a logr.LogSink through the slog.Handler and thus slog.Logger -// APIs. -// -// See the README in the top-level [./logr] package for a discussion of -// interoperability. -package slogr +package logr import ( "context" "log/slog" - - "github.com/go-logr/logr" ) -// NewLogr returns a logr.Logger which writes to the slog.Handler. +// FromSlogHandler returns a Logger which writes to the slog.Handler. // // The logr verbosity level is mapped to slog levels such that V(0) becomes // slog.LevelInfo and V(4) becomes slog.LevelDebug. -func NewLogr(handler slog.Handler) logr.Logger { +func FromSlogHandler(handler slog.Handler) Logger { if handler, ok := handler.(*slogHandler); ok { if handler.sink == nil { - return logr.Discard() + return Discard() } - return logr.New(handler.sink).V(int(handler.levelBias)) + return New(handler.sink).V(int(handler.levelBias)) } - return logr.New(&slogSink{handler: handler}) + return New(&slogSink{handler: handler}) } -// NewSlogHandler returns a slog.Handler which writes to the same sink as the logr.Logger. +// ToSlogHandler returns a slog.Handler which writes to the same sink as the Logger. // // The returned logger writes all records with level >= slog.LevelError as // error log entries with LogSink.Error, regardless of the verbosity level of -// the logr.Logger: +// the Logger: // -// logger := -// slog.New(NewSlogHandler(logger.V(10))).Error(...) -> logSink.Error(...) +// logger := +// slog.New(ToSlogHandler(logger.V(10))).Error(...) -> logSink.Error(...) // // The level of all other records gets reduced by the verbosity -// level of the logr.Logger and the result is negated. If it happens +// level of the Logger and the result is negated. If it happens // to be negative, then it gets replaced by zero because a LogSink // is not expected to handled negative levels: // -// slog.New(NewSlogHandler(logger)).Debug(...) -> logger.GetSink().Info(level=4, ...) -// slog.New(NewSlogHandler(logger)).Warning(...) -> logger.GetSink().Info(level=0, ...) -// slog.New(NewSlogHandler(logger)).Info(...) -> logger.GetSink().Info(level=0, ...) -// slog.New(NewSlogHandler(logger.V(4))).Info(...) -> logger.GetSink().Info(level=4, ...) -func NewSlogHandler(logger logr.Logger) slog.Handler { +// slog.New(ToSlogHandler(logger)).Debug(...) -> logger.GetSink().Info(level=4, ...) +// slog.New(ToSlogHandler(logger)).Warning(...) -> logger.GetSink().Info(level=0, ...) +// slog.New(ToSlogHandler(logger)).Info(...) -> logger.GetSink().Info(level=0, ...) +// slog.New(ToSlogHandler(logger.V(4))).Info(...) -> logger.GetSink().Info(level=4, ...) +func ToSlogHandler(logger Logger) slog.Handler { if sink, ok := logger.GetSink().(*slogSink); ok && logger.GetV() == 0 { return sink.handler } @@ -87,7 +79,7 @@ func NewSlogHandler(logger logr.Logger) slog.Handler { // - verbosity levels > slog.LevelInfo can be recorded // - less overhead // -// Both APIs (logr.Logger and slog.Logger/Handler) then are supported equally +// Both APIs (Logger and slog.Logger/Handler) then are supported equally // well. Developers can pick whatever API suits them better and/or mix // packages which use either API in the same binary with a common logging // implementation. @@ -97,10 +89,10 @@ func NewSlogHandler(logger logr.Logger) slog.Handler { // different prototype of the common Enabled method. // // An implementation could support both interfaces in two different types, but then -// additional interfaces would be needed to convert between those types in NewLogr -// and NewSlogHandler. +// additional interfaces would be needed to convert between those types in FromSlogHandler +// and ToSlogHandler. type SlogSink interface { - logr.LogSink + LogSink Handle(ctx context.Context, record slog.Record) error WithAttrs(attrs []slog.Attr) SlogSink diff --git a/vendor/github.com/go-logr/logr/slogr/slogsink.go b/vendor/github.com/go-logr/logr/slogsink.go similarity index 82% rename from vendor/github.com/go-logr/logr/slogr/slogsink.go rename to vendor/github.com/go-logr/logr/slogsink.go index 6fbac561..4060fcbc 100644 --- a/vendor/github.com/go-logr/logr/slogr/slogsink.go +++ b/vendor/github.com/go-logr/logr/slogsink.go @@ -17,24 +17,22 @@ See the License for the specific language governing permissions and limitations under the License. */ -package slogr +package logr import ( "context" "log/slog" "runtime" "time" - - "github.com/go-logr/logr" ) var ( - _ logr.LogSink = &slogSink{} - _ logr.CallDepthLogSink = &slogSink{} - _ Underlier = &slogSink{} + _ LogSink = &slogSink{} + _ CallDepthLogSink = &slogSink{} + _ Underlier = &slogSink{} ) -// Underlier is implemented by the LogSink returned by NewLogr. +// Underlier is implemented by the LogSink returned by NewFromLogHandler. type Underlier interface { // GetUnderlying returns the Handler used by the LogSink. GetUnderlying() slog.Handler @@ -54,7 +52,7 @@ type slogSink struct { handler slog.Handler } -func (l *slogSink) Init(info logr.RuntimeInfo) { +func (l *slogSink) Init(info RuntimeInfo) { l.callDepth = info.CallDepth } @@ -62,7 +60,7 @@ func (l *slogSink) GetUnderlying() slog.Handler { return l.handler } -func (l *slogSink) WithCallDepth(depth int) logr.LogSink { +func (l *slogSink) WithCallDepth(depth int) LogSink { newLogger := *l newLogger.callDepth += depth return &newLogger @@ -93,18 +91,18 @@ func (l *slogSink) log(err error, msg string, level slog.Level, kvList ...interf record.AddAttrs(slog.Any(errKey, err)) } record.Add(kvList...) - l.handler.Handle(context.Background(), record) + _ = l.handler.Handle(context.Background(), record) } -func (l slogSink) WithName(name string) logr.LogSink { +func (l slogSink) WithName(name string) LogSink { if l.name != "" { - l.name = l.name + "/" + l.name += "/" } l.name += name return &l } -func (l slogSink) WithValues(kvList ...interface{}) logr.LogSink { +func (l slogSink) WithValues(kvList ...interface{}) LogSink { l.handler = l.handler.WithAttrs(kvListToAttrs(kvList...)) return &l } diff --git a/vendor/github.com/go-ole/go-ole/.travis.yml b/vendor/github.com/go-ole/go-ole/.travis.yml new file mode 100644 index 00000000..28f740cd --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/.travis.yml @@ -0,0 +1,8 @@ +language: go +sudo: false + +go: + - 1.9.x + - 1.10.x + - 1.11.x + - tip diff --git a/vendor/github.com/go-ole/go-ole/ChangeLog.md b/vendor/github.com/go-ole/go-ole/ChangeLog.md new file mode 100644 index 00000000..4ba6a8c6 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/ChangeLog.md @@ -0,0 +1,49 @@ +# Version 1.x.x + +* **Add more test cases and reference new test COM server project.** (Placeholder for future additions) + +# Version 1.2.0-alphaX + +**Minimum supported version is now Go 1.4. Go 1.1 support is deprecated, but should still build.** + + * Added CI configuration for Travis-CI and AppVeyor. + * Added test InterfaceID and ClassID for the COM Test Server project. + * Added more inline documentation (#83). + * Added IEnumVARIANT implementation (#88). + * Added IEnumVARIANT test cases (#99, #100, #101). + * Added support for retrieving `time.Time` from VARIANT (#92). + * Added test case for IUnknown (#64). + * Added test case for IDispatch (#64). + * Added test cases for scalar variants (#64, #76). + +# Version 1.1.1 + + * Fixes for Linux build. + * Fixes for Windows build. + +# Version 1.1.0 + +The change to provide building on all platforms is a new feature. The increase in minor version reflects that and allows those who wish to stay on 1.0.x to continue to do so. Support for 1.0.x will be limited to bug fixes. + + * Move GUID out of variables.go into its own file to make new documentation available. + * Move OleError out of ole.go into its own file to make new documentation available. + * Add documentation to utility functions. + * Add documentation to variant receiver functions. + * Add documentation to ole structures. + * Make variant available to other systems outside of Windows. + * Make OLE structures available to other systems outside of Windows. + +## New Features + + * Library should now be built on all platforms supported by Go. Library will NOOP on any platform that is not Windows. + * More functions are now documented and available on godoc.org. + +# Version 1.0.1 + + 1. Fix package references from repository location change. + +# Version 1.0.0 + +This version is stable enough for use. The COM API is still incomplete, but provides enough functionality for accessing COM servers using IDispatch interface. + +There is no changelog for this version. Check commits for history. diff --git a/vendor/github.com/go-ole/go-ole/LICENSE b/vendor/github.com/go-ole/go-ole/LICENSE new file mode 100644 index 00000000..623ec06f --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2013-2017 Yasuhiro Matsumoto, + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/go-ole/go-ole/README.md b/vendor/github.com/go-ole/go-ole/README.md new file mode 100644 index 00000000..7b577558 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/README.md @@ -0,0 +1,46 @@ +# Go OLE + +[![Build status](https://ci.appveyor.com/api/projects/status/qr0u2sf7q43us9fj?svg=true)](https://ci.appveyor.com/project/jacobsantos/go-ole-jgs28) +[![Build Status](https://travis-ci.org/go-ole/go-ole.svg?branch=master)](https://travis-ci.org/go-ole/go-ole) +[![GoDoc](https://godoc.org/github.com/go-ole/go-ole?status.svg)](https://godoc.org/github.com/go-ole/go-ole) + +Go bindings for Windows COM using shared libraries instead of cgo. + +By Yasuhiro Matsumoto. + +## Install + +To experiment with go-ole, you can just compile and run the example program: + +``` +go get github.com/go-ole/go-ole +cd /path/to/go-ole/ +go test + +cd /path/to/go-ole/example/excel +go run excel.go +``` + +## Continuous Integration + +Continuous integration configuration has been added for both Travis-CI and AppVeyor. You will have to add these to your own account for your fork in order for it to run. + +**Travis-CI** + +Travis-CI was added to check builds on Linux to ensure that `go get` works when cross building. Currently, Travis-CI is not used to test cross-building, but this may be changed in the future. It is also not currently possible to test the library on Linux, since COM API is specific to Windows and it is not currently possible to run a COM server on Linux or even connect to a remote COM server. + +**AppVeyor** + +AppVeyor is used to build on Windows using the (in-development) test COM server. It is currently only used to test the build and ensure that the code works on Windows. It will be used to register a COM server and then run the test cases based on the test COM server. + +The tests currently do run and do pass and this should be maintained with commits. + +## Versioning + +Go OLE uses [semantic versioning](http://semver.org) for version numbers, which is similar to the version contract of the Go language. Which means that the major version will always maintain backwards compatibility with minor versions. Minor versions will only add new additions and changes. Fixes will always be in patch. + +This contract should allow you to upgrade to new minor and patch versions without breakage or modifications to your existing code. Leave a ticket, if there is breakage, so that it could be fixed. + +## LICENSE + +Under the MIT License: http://mattn.mit-license.org/2013 diff --git a/vendor/github.com/go-ole/go-ole/appveyor.yml b/vendor/github.com/go-ole/go-ole/appveyor.yml new file mode 100644 index 00000000..0d557ac2 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/appveyor.yml @@ -0,0 +1,54 @@ +# Notes: +# - Minimal appveyor.yml file is an empty file. All sections are optional. +# - Indent each level of configuration with 2 spaces. Do not use tabs! +# - All section names are case-sensitive. +# - Section names should be unique on each level. + +version: "1.3.0.{build}-alpha-{branch}" + +os: Windows Server 2012 R2 + +branches: + only: + - master + - v1.2 + - v1.1 + - v1.0 + +skip_tags: true + +clone_folder: c:\gopath\src\github.com\go-ole\go-ole + +environment: + GOPATH: c:\gopath + matrix: + - GOARCH: amd64 + GOVERSION: 1.5 + GOROOT: c:\go + DOWNLOADPLATFORM: "x64" + +install: + - choco install mingw + - SET PATH=c:\tools\mingw64\bin;%PATH% + # - Download COM Server + - ps: Start-FileDownload "https://github.com/go-ole/test-com-server/releases/download/v1.0.2/test-com-server-${env:DOWNLOADPLATFORM}.zip" + - 7z e test-com-server-%DOWNLOADPLATFORM%.zip -oc:\gopath\src\github.com\go-ole\go-ole > NUL + - c:\gopath\src\github.com\go-ole\go-ole\build\register-assembly.bat + # - set + - go version + - go env + - go get -u golang.org/x/tools/cmd/cover + - go get -u golang.org/x/tools/cmd/godoc + - go get -u golang.org/x/tools/cmd/stringer + +build_script: + - cd c:\gopath\src\github.com\go-ole\go-ole + - go get -v -t ./... + - go build + - go test -v -cover ./... + +# disable automatic tests +test: off + +# disable deployment +deploy: off diff --git a/vendor/github.com/go-ole/go-ole/com.go b/vendor/github.com/go-ole/go-ole/com.go new file mode 100644 index 00000000..a9bef150 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/com.go @@ -0,0 +1,344 @@ +// +build windows + +package ole + +import ( + "syscall" + "unicode/utf16" + "unsafe" +) + +var ( + procCoInitialize = modole32.NewProc("CoInitialize") + procCoInitializeEx = modole32.NewProc("CoInitializeEx") + procCoUninitialize = modole32.NewProc("CoUninitialize") + procCoCreateInstance = modole32.NewProc("CoCreateInstance") + procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") + procCLSIDFromProgID = modole32.NewProc("CLSIDFromProgID") + procCLSIDFromString = modole32.NewProc("CLSIDFromString") + procStringFromCLSID = modole32.NewProc("StringFromCLSID") + procStringFromIID = modole32.NewProc("StringFromIID") + procIIDFromString = modole32.NewProc("IIDFromString") + procCoGetObject = modole32.NewProc("CoGetObject") + procGetUserDefaultLCID = modkernel32.NewProc("GetUserDefaultLCID") + procCopyMemory = modkernel32.NewProc("RtlMoveMemory") + procVariantInit = modoleaut32.NewProc("VariantInit") + procVariantClear = modoleaut32.NewProc("VariantClear") + procVariantTimeToSystemTime = modoleaut32.NewProc("VariantTimeToSystemTime") + procSysAllocString = modoleaut32.NewProc("SysAllocString") + procSysAllocStringLen = modoleaut32.NewProc("SysAllocStringLen") + procSysFreeString = modoleaut32.NewProc("SysFreeString") + procSysStringLen = modoleaut32.NewProc("SysStringLen") + procCreateDispTypeInfo = modoleaut32.NewProc("CreateDispTypeInfo") + procCreateStdDispatch = modoleaut32.NewProc("CreateStdDispatch") + procGetActiveObject = modoleaut32.NewProc("GetActiveObject") + + procGetMessageW = moduser32.NewProc("GetMessageW") + procDispatchMessageW = moduser32.NewProc("DispatchMessageW") +) + +// coInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func coInitialize() (err error) { + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx + // Suggests that no value should be passed to CoInitialized. + // Could just be Call() since the parameter is optional. <-- Needs testing to be sure. + hr, _, _ := procCoInitialize.Call(uintptr(0)) + if hr != 0 { + err = NewError(hr) + } + return +} + +// coInitializeEx initializes COM library with concurrency model. +func coInitializeEx(coinit uint32) (err error) { + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx + // Suggests that the first parameter is not only optional but should always be NULL. + hr, _, _ := procCoInitializeEx.Call(uintptr(0), uintptr(coinit)) + if hr != 0 { + err = NewError(hr) + } + return +} + +// CoInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func CoInitialize(p uintptr) (err error) { + // p is ignored and won't be used. + // Avoid any variable not used errors. + p = uintptr(0) + return coInitialize() +} + +// CoInitializeEx initializes COM library with concurrency model. +func CoInitializeEx(p uintptr, coinit uint32) (err error) { + // Avoid any variable not used errors. + p = uintptr(0) + return coInitializeEx(coinit) +} + +// CoUninitialize uninitializes COM Library. +func CoUninitialize() { + procCoUninitialize.Call() +} + +// CoTaskMemFree frees memory pointer. +func CoTaskMemFree(memptr uintptr) { + procCoTaskMemFree.Call(memptr) +} + +// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier. +// +// The Programmatic Identifier must be registered, because it will be looked up +// in the Windows Registry. The registry entry has the following keys: CLSID, +// Insertable, Protocol and Shell +// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx). +// +// programID identifies the class id with less precision and is not guaranteed +// to be unique. These are usually found in the registry under +// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of +// "Program.Component.Version" with version being optional. +// +// CLSIDFromProgID in Windows API. +func CLSIDFromProgID(progId string) (clsid *GUID, err error) { + var guid GUID + lpszProgID := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId))) + hr, _, _ := procCLSIDFromProgID.Call(lpszProgID, uintptr(unsafe.Pointer(&guid))) + if hr != 0 { + err = NewError(hr) + } + clsid = &guid + return +} + +// CLSIDFromString retrieves Class ID from string representation. +// +// This is technically the string version of the GUID and will convert the +// string to object. +// +// CLSIDFromString in Windows API. +func CLSIDFromString(str string) (clsid *GUID, err error) { + var guid GUID + lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str))) + hr, _, _ := procCLSIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid))) + if hr != 0 { + err = NewError(hr) + } + clsid = &guid + return +} + +// StringFromCLSID returns GUID formated string from GUID object. +func StringFromCLSID(clsid *GUID) (str string, err error) { + var p *uint16 + hr, _, _ := procStringFromCLSID.Call(uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(&p))) + if hr != 0 { + err = NewError(hr) + } + str = LpOleStrToString(p) + return +} + +// IIDFromString returns GUID from program ID. +func IIDFromString(progId string) (clsid *GUID, err error) { + var guid GUID + lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId))) + hr, _, _ := procIIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid))) + if hr != 0 { + err = NewError(hr) + } + clsid = &guid + return +} + +// StringFromIID returns GUID formatted string from GUID object. +func StringFromIID(iid *GUID) (str string, err error) { + var p *uint16 + hr, _, _ := procStringFromIID.Call(uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&p))) + if hr != 0 { + err = NewError(hr) + } + str = LpOleStrToString(p) + return +} + +// CreateInstance of single uninitialized object with GUID. +func CreateInstance(clsid *GUID, iid *GUID) (unk *IUnknown, err error) { + if iid == nil { + iid = IID_IUnknown + } + hr, _, _ := procCoCreateInstance.Call( + uintptr(unsafe.Pointer(clsid)), + 0, + CLSCTX_SERVER, + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&unk))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// GetActiveObject retrieves pointer to active object. +func GetActiveObject(clsid *GUID, iid *GUID) (unk *IUnknown, err error) { + if iid == nil { + iid = IID_IUnknown + } + hr, _, _ := procGetActiveObject.Call( + uintptr(unsafe.Pointer(clsid)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&unk))) + if hr != 0 { + err = NewError(hr) + } + return +} + +type BindOpts struct { + CbStruct uint32 + GrfFlags uint32 + GrfMode uint32 + TickCountDeadline uint32 +} + +// GetObject retrieves pointer to active object. +func GetObject(programID string, bindOpts *BindOpts, iid *GUID) (unk *IUnknown, err error) { + if bindOpts != nil { + bindOpts.CbStruct = uint32(unsafe.Sizeof(BindOpts{})) + } + if iid == nil { + iid = IID_IUnknown + } + hr, _, _ := procCoGetObject.Call( + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(programID))), + uintptr(unsafe.Pointer(bindOpts)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&unk))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// VariantInit initializes variant. +func VariantInit(v *VARIANT) (err error) { + hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// VariantClear clears value in Variant settings to VT_EMPTY. +func VariantClear(v *VARIANT) (err error) { + hr, _, _ := procVariantClear.Call(uintptr(unsafe.Pointer(v))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// SysAllocString allocates memory for string and copies string into memory. +func SysAllocString(v string) (ss *int16) { + pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v)))) + ss = (*int16)(unsafe.Pointer(pss)) + return +} + +// SysAllocStringLen copies up to length of given string returning pointer. +func SysAllocStringLen(v string) (ss *int16) { + utf16 := utf16.Encode([]rune(v + "\x00")) + ptr := &utf16[0] + + pss, _, _ := procSysAllocStringLen.Call(uintptr(unsafe.Pointer(ptr)), uintptr(len(utf16)-1)) + ss = (*int16)(unsafe.Pointer(pss)) + return +} + +// SysFreeString frees string system memory. This must be called with SysAllocString. +func SysFreeString(v *int16) (err error) { + hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// SysStringLen is the length of the system allocated string. +func SysStringLen(v *int16) uint32 { + l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v))) + return uint32(l) +} + +// CreateStdDispatch provides default IDispatch implementation for IUnknown. +// +// This handles default IDispatch implementation for objects. It haves a few +// limitations with only supporting one language. It will also only return +// default exception codes. +func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (disp *IDispatch, err error) { + hr, _, _ := procCreateStdDispatch.Call( + uintptr(unsafe.Pointer(unk)), + v, + uintptr(unsafe.Pointer(ptinfo)), + uintptr(unsafe.Pointer(&disp))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch. +// +// This will not handle the full implementation of the interface. +func CreateDispTypeInfo(idata *INTERFACEDATA) (pptinfo *IUnknown, err error) { + hr, _, _ := procCreateDispTypeInfo.Call( + uintptr(unsafe.Pointer(idata)), + uintptr(GetUserDefaultLCID()), + uintptr(unsafe.Pointer(&pptinfo))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// copyMemory moves location of a block of memory. +func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) { + procCopyMemory.Call(uintptr(dest), uintptr(src), uintptr(length)) +} + +// GetUserDefaultLCID retrieves current user default locale. +func GetUserDefaultLCID() (lcid uint32) { + ret, _, _ := procGetUserDefaultLCID.Call() + lcid = uint32(ret) + return +} + +// GetMessage in message queue from runtime. +// +// This function appears to block. PeekMessage does not block. +func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) { + r0, _, err := procGetMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax)) + ret = int32(r0) + return +} + +// DispatchMessage to window procedure. +func DispatchMessage(msg *Msg) (ret int32) { + r0, _, _ := procDispatchMessageW.Call(uintptr(unsafe.Pointer(msg))) + ret = int32(r0) + return +} diff --git a/vendor/github.com/go-ole/go-ole/com_func.go b/vendor/github.com/go-ole/go-ole/com_func.go new file mode 100644 index 00000000..cef539d9 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/com_func.go @@ -0,0 +1,174 @@ +// +build !windows + +package ole + +import ( + "time" + "unsafe" +) + +// coInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func coInitialize() error { + return NewError(E_NOTIMPL) +} + +// coInitializeEx initializes COM library with concurrency model. +func coInitializeEx(coinit uint32) error { + return NewError(E_NOTIMPL) +} + +// CoInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func CoInitialize(p uintptr) error { + return NewError(E_NOTIMPL) +} + +// CoInitializeEx initializes COM library with concurrency model. +func CoInitializeEx(p uintptr, coinit uint32) error { + return NewError(E_NOTIMPL) +} + +// CoUninitialize uninitializes COM Library. +func CoUninitialize() {} + +// CoTaskMemFree frees memory pointer. +func CoTaskMemFree(memptr uintptr) {} + +// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier. +// +// The Programmatic Identifier must be registered, because it will be looked up +// in the Windows Registry. The registry entry has the following keys: CLSID, +// Insertable, Protocol and Shell +// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx). +// +// programID identifies the class id with less precision and is not guaranteed +// to be unique. These are usually found in the registry under +// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of +// "Program.Component.Version" with version being optional. +// +// CLSIDFromProgID in Windows API. +func CLSIDFromProgID(progId string) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// CLSIDFromString retrieves Class ID from string representation. +// +// This is technically the string version of the GUID and will convert the +// string to object. +// +// CLSIDFromString in Windows API. +func CLSIDFromString(str string) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// StringFromCLSID returns GUID formated string from GUID object. +func StringFromCLSID(clsid *GUID) (string, error) { + return "", NewError(E_NOTIMPL) +} + +// IIDFromString returns GUID from program ID. +func IIDFromString(progId string) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// StringFromIID returns GUID formatted string from GUID object. +func StringFromIID(iid *GUID) (string, error) { + return "", NewError(E_NOTIMPL) +} + +// CreateInstance of single uninitialized object with GUID. +func CreateInstance(clsid *GUID, iid *GUID) (*IUnknown, error) { + return nil, NewError(E_NOTIMPL) +} + +// GetActiveObject retrieves pointer to active object. +func GetActiveObject(clsid *GUID, iid *GUID) (*IUnknown, error) { + return nil, NewError(E_NOTIMPL) +} + +// VariantInit initializes variant. +func VariantInit(v *VARIANT) error { + return NewError(E_NOTIMPL) +} + +// VariantClear clears value in Variant settings to VT_EMPTY. +func VariantClear(v *VARIANT) error { + return NewError(E_NOTIMPL) +} + +// SysAllocString allocates memory for string and copies string into memory. +func SysAllocString(v string) *int16 { + u := int16(0) + return &u +} + +// SysAllocStringLen copies up to length of given string returning pointer. +func SysAllocStringLen(v string) *int16 { + u := int16(0) + return &u +} + +// SysFreeString frees string system memory. This must be called with SysAllocString. +func SysFreeString(v *int16) error { + return NewError(E_NOTIMPL) +} + +// SysStringLen is the length of the system allocated string. +func SysStringLen(v *int16) uint32 { + return uint32(0) +} + +// CreateStdDispatch provides default IDispatch implementation for IUnknown. +// +// This handles default IDispatch implementation for objects. It haves a few +// limitations with only supporting one language. It will also only return +// default exception codes. +func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (*IDispatch, error) { + return nil, NewError(E_NOTIMPL) +} + +// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch. +// +// This will not handle the full implementation of the interface. +func CreateDispTypeInfo(idata *INTERFACEDATA) (*IUnknown, error) { + return nil, NewError(E_NOTIMPL) +} + +// copyMemory moves location of a block of memory. +func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {} + +// GetUserDefaultLCID retrieves current user default locale. +func GetUserDefaultLCID() uint32 { + return uint32(0) +} + +// GetMessage in message queue from runtime. +// +// This function appears to block. PeekMessage does not block. +func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (int32, error) { + return int32(0), NewError(E_NOTIMPL) +} + +// DispatchMessage to window procedure. +func DispatchMessage(msg *Msg) int32 { + return int32(0) +} + +func GetVariantDate(value uint64) (time.Time, error) { + return time.Now(), NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/connect.go b/vendor/github.com/go-ole/go-ole/connect.go new file mode 100644 index 00000000..b2ac2ec6 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/connect.go @@ -0,0 +1,192 @@ +package ole + +// Connection contains IUnknown for fluent interface interaction. +// +// Deprecated. Use oleutil package instead. +type Connection struct { + Object *IUnknown // Access COM +} + +// Initialize COM. +func (*Connection) Initialize() (err error) { + return coInitialize() +} + +// Uninitialize COM. +func (*Connection) Uninitialize() { + CoUninitialize() +} + +// Create IUnknown object based first on ProgId and then from String. +func (c *Connection) Create(progId string) (err error) { + var clsid *GUID + clsid, err = CLSIDFromProgID(progId) + if err != nil { + clsid, err = CLSIDFromString(progId) + if err != nil { + return + } + } + + unknown, err := CreateInstance(clsid, IID_IUnknown) + if err != nil { + return + } + c.Object = unknown + + return +} + +// Release IUnknown object. +func (c *Connection) Release() { + c.Object.Release() +} + +// Load COM object from list of programIDs or strings. +func (c *Connection) Load(names ...string) (errors []error) { + var tempErrors []error = make([]error, len(names)) + var numErrors int = 0 + for _, name := range names { + err := c.Create(name) + if err != nil { + tempErrors = append(tempErrors, err) + numErrors += 1 + continue + } + break + } + + copy(errors, tempErrors[0:numErrors]) + return +} + +// Dispatch returns Dispatch object. +func (c *Connection) Dispatch() (object *Dispatch, err error) { + dispatch, err := c.Object.QueryInterface(IID_IDispatch) + if err != nil { + return + } + object = &Dispatch{dispatch} + return +} + +// Dispatch stores IDispatch object. +type Dispatch struct { + Object *IDispatch // Dispatch object. +} + +// Call method on IDispatch with parameters. +func (d *Dispatch) Call(method string, params ...interface{}) (result *VARIANT, err error) { + id, err := d.GetId(method) + if err != nil { + return + } + + result, err = d.Invoke(id, DISPATCH_METHOD, params) + return +} + +// MustCall method on IDispatch with parameters. +func (d *Dispatch) MustCall(method string, params ...interface{}) (result *VARIANT) { + id, err := d.GetId(method) + if err != nil { + panic(err) + } + + result, err = d.Invoke(id, DISPATCH_METHOD, params) + if err != nil { + panic(err) + } + + return +} + +// Get property on IDispatch with parameters. +func (d *Dispatch) Get(name string, params ...interface{}) (result *VARIANT, err error) { + id, err := d.GetId(name) + if err != nil { + return + } + result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params) + return +} + +// MustGet property on IDispatch with parameters. +func (d *Dispatch) MustGet(name string, params ...interface{}) (result *VARIANT) { + id, err := d.GetId(name) + if err != nil { + panic(err) + } + + result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params) + if err != nil { + panic(err) + } + return +} + +// Set property on IDispatch with parameters. +func (d *Dispatch) Set(name string, params ...interface{}) (result *VARIANT, err error) { + id, err := d.GetId(name) + if err != nil { + return + } + result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params) + return +} + +// MustSet property on IDispatch with parameters. +func (d *Dispatch) MustSet(name string, params ...interface{}) (result *VARIANT) { + id, err := d.GetId(name) + if err != nil { + panic(err) + } + + result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params) + if err != nil { + panic(err) + } + return +} + +// GetId retrieves ID of name on IDispatch. +func (d *Dispatch) GetId(name string) (id int32, err error) { + var dispid []int32 + dispid, err = d.Object.GetIDsOfName([]string{name}) + if err != nil { + return + } + id = dispid[0] + return +} + +// GetIds retrieves all IDs of names on IDispatch. +func (d *Dispatch) GetIds(names ...string) (dispid []int32, err error) { + dispid, err = d.Object.GetIDsOfName(names) + return +} + +// Invoke IDispatch on DisplayID of dispatch type with parameters. +// +// There have been problems where if send cascading params..., it would error +// out because the parameters would be empty. +func (d *Dispatch) Invoke(id int32, dispatch int16, params []interface{}) (result *VARIANT, err error) { + if len(params) < 1 { + result, err = d.Object.Invoke(id, dispatch) + } else { + result, err = d.Object.Invoke(id, dispatch, params...) + } + return +} + +// Release IDispatch object. +func (d *Dispatch) Release() { + d.Object.Release() +} + +// Connect initializes COM and attempts to load IUnknown based on given names. +func Connect(names ...string) (connection *Connection) { + connection.Initialize() + connection.Load(names...) + return +} diff --git a/vendor/github.com/go-ole/go-ole/constants.go b/vendor/github.com/go-ole/go-ole/constants.go new file mode 100644 index 00000000..fd0c6d74 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/constants.go @@ -0,0 +1,153 @@ +package ole + +const ( + CLSCTX_INPROC_SERVER = 1 + CLSCTX_INPROC_HANDLER = 2 + CLSCTX_LOCAL_SERVER = 4 + CLSCTX_INPROC_SERVER16 = 8 + CLSCTX_REMOTE_SERVER = 16 + CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER + CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER + CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER +) + +const ( + COINIT_APARTMENTTHREADED = 0x2 + COINIT_MULTITHREADED = 0x0 + COINIT_DISABLE_OLE1DDE = 0x4 + COINIT_SPEED_OVER_MEMORY = 0x8 +) + +const ( + DISPATCH_METHOD = 1 + DISPATCH_PROPERTYGET = 2 + DISPATCH_PROPERTYPUT = 4 + DISPATCH_PROPERTYPUTREF = 8 +) + +const ( + S_OK = 0x00000000 + E_UNEXPECTED = 0x8000FFFF + E_NOTIMPL = 0x80004001 + E_OUTOFMEMORY = 0x8007000E + E_INVALIDARG = 0x80070057 + E_NOINTERFACE = 0x80004002 + E_POINTER = 0x80004003 + E_HANDLE = 0x80070006 + E_ABORT = 0x80004004 + E_FAIL = 0x80004005 + E_ACCESSDENIED = 0x80070005 + E_PENDING = 0x8000000A + + CO_E_CLASSSTRING = 0x800401F3 +) + +const ( + CC_FASTCALL = iota + CC_CDECL + CC_MSCPASCAL + CC_PASCAL = CC_MSCPASCAL + CC_MACPASCAL + CC_STDCALL + CC_FPFASTCALL + CC_SYSCALL + CC_MPWCDECL + CC_MPWPASCAL + CC_MAX = CC_MPWPASCAL +) + +type VT uint16 + +const ( + VT_EMPTY VT = 0x0 + VT_NULL VT = 0x1 + VT_I2 VT = 0x2 + VT_I4 VT = 0x3 + VT_R4 VT = 0x4 + VT_R8 VT = 0x5 + VT_CY VT = 0x6 + VT_DATE VT = 0x7 + VT_BSTR VT = 0x8 + VT_DISPATCH VT = 0x9 + VT_ERROR VT = 0xa + VT_BOOL VT = 0xb + VT_VARIANT VT = 0xc + VT_UNKNOWN VT = 0xd + VT_DECIMAL VT = 0xe + VT_I1 VT = 0x10 + VT_UI1 VT = 0x11 + VT_UI2 VT = 0x12 + VT_UI4 VT = 0x13 + VT_I8 VT = 0x14 + VT_UI8 VT = 0x15 + VT_INT VT = 0x16 + VT_UINT VT = 0x17 + VT_VOID VT = 0x18 + VT_HRESULT VT = 0x19 + VT_PTR VT = 0x1a + VT_SAFEARRAY VT = 0x1b + VT_CARRAY VT = 0x1c + VT_USERDEFINED VT = 0x1d + VT_LPSTR VT = 0x1e + VT_LPWSTR VT = 0x1f + VT_RECORD VT = 0x24 + VT_INT_PTR VT = 0x25 + VT_UINT_PTR VT = 0x26 + VT_FILETIME VT = 0x40 + VT_BLOB VT = 0x41 + VT_STREAM VT = 0x42 + VT_STORAGE VT = 0x43 + VT_STREAMED_OBJECT VT = 0x44 + VT_STORED_OBJECT VT = 0x45 + VT_BLOB_OBJECT VT = 0x46 + VT_CF VT = 0x47 + VT_CLSID VT = 0x48 + VT_BSTR_BLOB VT = 0xfff + VT_VECTOR VT = 0x1000 + VT_ARRAY VT = 0x2000 + VT_BYREF VT = 0x4000 + VT_RESERVED VT = 0x8000 + VT_ILLEGAL VT = 0xffff + VT_ILLEGALMASKED VT = 0xfff + VT_TYPEMASK VT = 0xfff +) + +const ( + DISPID_UNKNOWN = -1 + DISPID_VALUE = 0 + DISPID_PROPERTYPUT = -3 + DISPID_NEWENUM = -4 + DISPID_EVALUATE = -5 + DISPID_CONSTRUCTOR = -6 + DISPID_DESTRUCTOR = -7 + DISPID_COLLECT = -8 +) + +const ( + TKIND_ENUM = 1 + TKIND_RECORD = 2 + TKIND_MODULE = 3 + TKIND_INTERFACE = 4 + TKIND_DISPATCH = 5 + TKIND_COCLASS = 6 + TKIND_ALIAS = 7 + TKIND_UNION = 8 + TKIND_MAX = 9 +) + +// Safe Array Feature Flags + +const ( + FADF_AUTO = 0x0001 + FADF_STATIC = 0x0002 + FADF_EMBEDDED = 0x0004 + FADF_FIXEDSIZE = 0x0010 + FADF_RECORD = 0x0020 + FADF_HAVEIID = 0x0040 + FADF_HAVEVARTYPE = 0x0080 + FADF_BSTR = 0x0100 + FADF_UNKNOWN = 0x0200 + FADF_DISPATCH = 0x0400 + FADF_VARIANT = 0x0800 + FADF_RESERVED = 0xF008 +) diff --git a/vendor/github.com/go-ole/go-ole/error.go b/vendor/github.com/go-ole/go-ole/error.go new file mode 100644 index 00000000..096b456d --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/error.go @@ -0,0 +1,51 @@ +package ole + +// OleError stores COM errors. +type OleError struct { + hr uintptr + description string + subError error +} + +// NewError creates new error with HResult. +func NewError(hr uintptr) *OleError { + return &OleError{hr: hr} +} + +// NewErrorWithDescription creates new COM error with HResult and description. +func NewErrorWithDescription(hr uintptr, description string) *OleError { + return &OleError{hr: hr, description: description} +} + +// NewErrorWithSubError creates new COM error with parent error. +func NewErrorWithSubError(hr uintptr, description string, err error) *OleError { + return &OleError{hr: hr, description: description, subError: err} +} + +// Code is the HResult. +func (v *OleError) Code() uintptr { + return uintptr(v.hr) +} + +// String description, either manually set or format message with error code. +func (v *OleError) String() string { + if v.description != "" { + return errstr(int(v.hr)) + " (" + v.description + ")" + } + return errstr(int(v.hr)) +} + +// Error implements error interface. +func (v *OleError) Error() string { + return v.String() +} + +// Description retrieves error summary, if there is one. +func (v *OleError) Description() string { + return v.description +} + +// SubError returns parent error, if there is one. +func (v *OleError) SubError() error { + return v.subError +} diff --git a/vendor/github.com/go-ole/go-ole/error_func.go b/vendor/github.com/go-ole/go-ole/error_func.go new file mode 100644 index 00000000..8a2ffaa2 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/error_func.go @@ -0,0 +1,8 @@ +// +build !windows + +package ole + +// errstr converts error code to string. +func errstr(errno int) string { + return "" +} diff --git a/vendor/github.com/go-ole/go-ole/error_windows.go b/vendor/github.com/go-ole/go-ole/error_windows.go new file mode 100644 index 00000000..d0e8e685 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/error_windows.go @@ -0,0 +1,24 @@ +// +build windows + +package ole + +import ( + "fmt" + "syscall" + "unicode/utf16" +) + +// errstr converts error code to string. +func errstr(errno int) string { + // ask windows for the remaining errors + var flags uint32 = syscall.FORMAT_MESSAGE_FROM_SYSTEM | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS + b := make([]uint16, 300) + n, err := syscall.FormatMessage(flags, 0, uint32(errno), 0, b, nil) + if err != nil { + return fmt.Sprintf("error %d (FormatMessage failed with: %v)", errno, err) + } + // trim terminating \r and \n + for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- { + } + return string(utf16.Decode(b[:n])) +} diff --git a/vendor/github.com/go-ole/go-ole/guid.go b/vendor/github.com/go-ole/go-ole/guid.go new file mode 100644 index 00000000..8d20f68f --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/guid.go @@ -0,0 +1,284 @@ +package ole + +var ( + // IID_NULL is null Interface ID, used when no other Interface ID is known. + IID_NULL = NewGUID("{00000000-0000-0000-0000-000000000000}") + + // IID_IUnknown is for IUnknown interfaces. + IID_IUnknown = NewGUID("{00000000-0000-0000-C000-000000000046}") + + // IID_IDispatch is for IDispatch interfaces. + IID_IDispatch = NewGUID("{00020400-0000-0000-C000-000000000046}") + + // IID_IEnumVariant is for IEnumVariant interfaces + IID_IEnumVariant = NewGUID("{00020404-0000-0000-C000-000000000046}") + + // IID_IConnectionPointContainer is for IConnectionPointContainer interfaces. + IID_IConnectionPointContainer = NewGUID("{B196B284-BAB4-101A-B69C-00AA00341D07}") + + // IID_IConnectionPoint is for IConnectionPoint interfaces. + IID_IConnectionPoint = NewGUID("{B196B286-BAB4-101A-B69C-00AA00341D07}") + + // IID_IInspectable is for IInspectable interfaces. + IID_IInspectable = NewGUID("{AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90}") + + // IID_IProvideClassInfo is for IProvideClassInfo interfaces. + IID_IProvideClassInfo = NewGUID("{B196B283-BAB4-101A-B69C-00AA00341D07}") +) + +// These are for testing and not part of any library. +var ( + // IID_ICOMTestString is for ICOMTestString interfaces. + // + // {E0133EB4-C36F-469A-9D3D-C66B84BE19ED} + IID_ICOMTestString = NewGUID("{E0133EB4-C36F-469A-9D3D-C66B84BE19ED}") + + // IID_ICOMTestInt8 is for ICOMTestInt8 interfaces. + // + // {BEB06610-EB84-4155-AF58-E2BFF53680B4} + IID_ICOMTestInt8 = NewGUID("{BEB06610-EB84-4155-AF58-E2BFF53680B4}") + + // IID_ICOMTestInt16 is for ICOMTestInt16 interfaces. + // + // {DAA3F9FA-761E-4976-A860-8364CE55F6FC} + IID_ICOMTestInt16 = NewGUID("{DAA3F9FA-761E-4976-A860-8364CE55F6FC}") + + // IID_ICOMTestInt32 is for ICOMTestInt32 interfaces. + // + // {E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0} + IID_ICOMTestInt32 = NewGUID("{E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0}") + + // IID_ICOMTestInt64 is for ICOMTestInt64 interfaces. + // + // {8D437CBC-B3ED-485C-BC32-C336432A1623} + IID_ICOMTestInt64 = NewGUID("{8D437CBC-B3ED-485C-BC32-C336432A1623}") + + // IID_ICOMTestFloat is for ICOMTestFloat interfaces. + // + // {BF1ED004-EA02-456A-AA55-2AC8AC6B054C} + IID_ICOMTestFloat = NewGUID("{BF1ED004-EA02-456A-AA55-2AC8AC6B054C}") + + // IID_ICOMTestDouble is for ICOMTestDouble interfaces. + // + // {BF908A81-8687-4E93-999F-D86FAB284BA0} + IID_ICOMTestDouble = NewGUID("{BF908A81-8687-4E93-999F-D86FAB284BA0}") + + // IID_ICOMTestBoolean is for ICOMTestBoolean interfaces. + // + // {D530E7A6-4EE8-40D1-8931-3D63B8605010} + IID_ICOMTestBoolean = NewGUID("{D530E7A6-4EE8-40D1-8931-3D63B8605010}") + + // IID_ICOMEchoTestObject is for ICOMEchoTestObject interfaces. + // + // {6485B1EF-D780-4834-A4FE-1EBB51746CA3} + IID_ICOMEchoTestObject = NewGUID("{6485B1EF-D780-4834-A4FE-1EBB51746CA3}") + + // IID_ICOMTestTypes is for ICOMTestTypes interfaces. + // + // {CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0} + IID_ICOMTestTypes = NewGUID("{CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0}") + + // CLSID_COMEchoTestObject is for COMEchoTestObject class. + // + // {3C24506A-AE9E-4D50-9157-EF317281F1B0} + CLSID_COMEchoTestObject = NewGUID("{3C24506A-AE9E-4D50-9157-EF317281F1B0}") + + // CLSID_COMTestScalarClass is for COMTestScalarClass class. + // + // {865B85C5-0334-4AC6-9EF6-AACEC8FC5E86} + CLSID_COMTestScalarClass = NewGUID("{865B85C5-0334-4AC6-9EF6-AACEC8FC5E86}") +) + +const hextable = "0123456789ABCDEF" +const emptyGUID = "{00000000-0000-0000-0000-000000000000}" + +// GUID is Windows API specific GUID type. +// +// This exists to match Windows GUID type for direct passing for COM. +// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx. +type GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + +// NewGUID converts the given string into a globally unique identifier that is +// compliant with the Windows API. +// +// The supplied string may be in any of these formats: +// +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX +// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} +// +// The conversion of the supplied string is not case-sensitive. +func NewGUID(guid string) *GUID { + d := []byte(guid) + var d1, d2, d3, d4a, d4b []byte + + switch len(d) { + case 38: + if d[0] != '{' || d[37] != '}' { + return nil + } + d = d[1:37] + fallthrough + case 36: + if d[8] != '-' || d[13] != '-' || d[18] != '-' || d[23] != '-' { + return nil + } + d1 = d[0:8] + d2 = d[9:13] + d3 = d[14:18] + d4a = d[19:23] + d4b = d[24:36] + case 32: + d1 = d[0:8] + d2 = d[8:12] + d3 = d[12:16] + d4a = d[16:20] + d4b = d[20:32] + default: + return nil + } + + var g GUID + var ok1, ok2, ok3, ok4 bool + g.Data1, ok1 = decodeHexUint32(d1) + g.Data2, ok2 = decodeHexUint16(d2) + g.Data3, ok3 = decodeHexUint16(d3) + g.Data4, ok4 = decodeHexByte64(d4a, d4b) + if ok1 && ok2 && ok3 && ok4 { + return &g + } + return nil +} + +func decodeHexUint32(src []byte) (value uint32, ok bool) { + var b1, b2, b3, b4 byte + var ok1, ok2, ok3, ok4 bool + b1, ok1 = decodeHexByte(src[0], src[1]) + b2, ok2 = decodeHexByte(src[2], src[3]) + b3, ok3 = decodeHexByte(src[4], src[5]) + b4, ok4 = decodeHexByte(src[6], src[7]) + value = (uint32(b1) << 24) | (uint32(b2) << 16) | (uint32(b3) << 8) | uint32(b4) + ok = ok1 && ok2 && ok3 && ok4 + return +} + +func decodeHexUint16(src []byte) (value uint16, ok bool) { + var b1, b2 byte + var ok1, ok2 bool + b1, ok1 = decodeHexByte(src[0], src[1]) + b2, ok2 = decodeHexByte(src[2], src[3]) + value = (uint16(b1) << 8) | uint16(b2) + ok = ok1 && ok2 + return +} + +func decodeHexByte64(s1 []byte, s2 []byte) (value [8]byte, ok bool) { + var ok1, ok2, ok3, ok4, ok5, ok6, ok7, ok8 bool + value[0], ok1 = decodeHexByte(s1[0], s1[1]) + value[1], ok2 = decodeHexByte(s1[2], s1[3]) + value[2], ok3 = decodeHexByte(s2[0], s2[1]) + value[3], ok4 = decodeHexByte(s2[2], s2[3]) + value[4], ok5 = decodeHexByte(s2[4], s2[5]) + value[5], ok6 = decodeHexByte(s2[6], s2[7]) + value[6], ok7 = decodeHexByte(s2[8], s2[9]) + value[7], ok8 = decodeHexByte(s2[10], s2[11]) + ok = ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8 + return +} + +func decodeHexByte(c1, c2 byte) (value byte, ok bool) { + var n1, n2 byte + var ok1, ok2 bool + n1, ok1 = decodeHexChar(c1) + n2, ok2 = decodeHexChar(c2) + value = (n1 << 4) | n2 + ok = ok1 && ok2 + return +} + +func decodeHexChar(c byte) (byte, bool) { + switch { + case '0' <= c && c <= '9': + return c - '0', true + case 'a' <= c && c <= 'f': + return c - 'a' + 10, true + case 'A' <= c && c <= 'F': + return c - 'A' + 10, true + } + + return 0, false +} + +// String converts the GUID to string form. It will adhere to this pattern: +// +// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} +// +// If the GUID is nil, the string representation of an empty GUID is returned: +// +// {00000000-0000-0000-0000-000000000000} +func (guid *GUID) String() string { + if guid == nil { + return emptyGUID + } + + var c [38]byte + c[0] = '{' + putUint32Hex(c[1:9], guid.Data1) + c[9] = '-' + putUint16Hex(c[10:14], guid.Data2) + c[14] = '-' + putUint16Hex(c[15:19], guid.Data3) + c[19] = '-' + putByteHex(c[20:24], guid.Data4[0:2]) + c[24] = '-' + putByteHex(c[25:37], guid.Data4[2:8]) + c[37] = '}' + return string(c[:]) +} + +func putUint32Hex(b []byte, v uint32) { + b[0] = hextable[byte(v>>24)>>4] + b[1] = hextable[byte(v>>24)&0x0f] + b[2] = hextable[byte(v>>16)>>4] + b[3] = hextable[byte(v>>16)&0x0f] + b[4] = hextable[byte(v>>8)>>4] + b[5] = hextable[byte(v>>8)&0x0f] + b[6] = hextable[byte(v)>>4] + b[7] = hextable[byte(v)&0x0f] +} + +func putUint16Hex(b []byte, v uint16) { + b[0] = hextable[byte(v>>8)>>4] + b[1] = hextable[byte(v>>8)&0x0f] + b[2] = hextable[byte(v)>>4] + b[3] = hextable[byte(v)&0x0f] +} + +func putByteHex(dst, src []byte) { + for i := 0; i < len(src); i++ { + dst[i*2] = hextable[src[i]>>4] + dst[i*2+1] = hextable[src[i]&0x0f] + } +} + +// IsEqualGUID compares two GUID. +// +// Not constant time comparison. +func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool { + return guid1.Data1 == guid2.Data1 && + guid1.Data2 == guid2.Data2 && + guid1.Data3 == guid2.Data3 && + guid1.Data4[0] == guid2.Data4[0] && + guid1.Data4[1] == guid2.Data4[1] && + guid1.Data4[2] == guid2.Data4[2] && + guid1.Data4[3] == guid2.Data4[3] && + guid1.Data4[4] == guid2.Data4[4] && + guid1.Data4[5] == guid2.Data4[5] && + guid1.Data4[6] == guid2.Data4[6] && + guid1.Data4[7] == guid2.Data4[7] +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpoint.go b/vendor/github.com/go-ole/go-ole/iconnectionpoint.go new file mode 100644 index 00000000..9e6c49f4 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpoint.go @@ -0,0 +1,20 @@ +package ole + +import "unsafe" + +type IConnectionPoint struct { + IUnknown +} + +type IConnectionPointVtbl struct { + IUnknownVtbl + GetConnectionInterface uintptr + GetConnectionPointContainer uintptr + Advise uintptr + Unadvise uintptr + EnumConnections uintptr +} + +func (v *IConnectionPoint) VTable() *IConnectionPointVtbl { + return (*IConnectionPointVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go b/vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go new file mode 100644 index 00000000..5414dc3c --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go @@ -0,0 +1,21 @@ +// +build !windows + +package ole + +import "unsafe" + +func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 { + return int32(0) +} + +func (v *IConnectionPoint) Advise(unknown *IUnknown) (uint32, error) { + return uint32(0), NewError(E_NOTIMPL) +} + +func (v *IConnectionPoint) Unadvise(cookie uint32) error { + return NewError(E_NOTIMPL) +} + +func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) (err error) { + return NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go b/vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go new file mode 100644 index 00000000..32bc1832 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go @@ -0,0 +1,43 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 { + // XXX: This doesn't look like it does what it's supposed to + return release((*IUnknown)(unsafe.Pointer(v))) +} + +func (v *IConnectionPoint) Advise(unknown *IUnknown) (cookie uint32, err error) { + hr, _, _ := syscall.Syscall( + v.VTable().Advise, + 3, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(unknown)), + uintptr(unsafe.Pointer(&cookie))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (v *IConnectionPoint) Unadvise(cookie uint32) (err error) { + hr, _, _ := syscall.Syscall( + v.VTable().Unadvise, + 2, + uintptr(unsafe.Pointer(v)), + uintptr(cookie), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) error { + return NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go new file mode 100644 index 00000000..165860d1 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go @@ -0,0 +1,17 @@ +package ole + +import "unsafe" + +type IConnectionPointContainer struct { + IUnknown +} + +type IConnectionPointContainerVtbl struct { + IUnknownVtbl + EnumConnectionPoints uintptr + FindConnectionPoint uintptr +} + +func (v *IConnectionPointContainer) VTable() *IConnectionPointContainerVtbl { + return (*IConnectionPointContainerVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go new file mode 100644 index 00000000..5dfa42aa --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go @@ -0,0 +1,11 @@ +// +build !windows + +package ole + +func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error { + return NewError(E_NOTIMPL) +} + +func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) error { + return NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go new file mode 100644 index 00000000..ad30d79e --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go @@ -0,0 +1,25 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error { + return NewError(E_NOTIMPL) +} + +func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) (err error) { + hr, _, _ := syscall.Syscall( + v.VTable().FindConnectionPoint, + 3, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(point))) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/idispatch.go b/vendor/github.com/go-ole/go-ole/idispatch.go new file mode 100644 index 00000000..d4af1240 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/idispatch.go @@ -0,0 +1,94 @@ +package ole + +import "unsafe" + +type IDispatch struct { + IUnknown +} + +type IDispatchVtbl struct { + IUnknownVtbl + GetTypeInfoCount uintptr + GetTypeInfo uintptr + GetIDsOfNames uintptr + Invoke uintptr +} + +func (v *IDispatch) VTable() *IDispatchVtbl { + return (*IDispatchVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *IDispatch) GetIDsOfName(names []string) (dispid []int32, err error) { + dispid, err = getIDsOfName(v, names) + return +} + +func (v *IDispatch) Invoke(dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) { + result, err = invoke(v, dispid, dispatch, params...) + return +} + +func (v *IDispatch) GetTypeInfoCount() (c uint32, err error) { + c, err = getTypeInfoCount(v) + return +} + +func (v *IDispatch) GetTypeInfo() (tinfo *ITypeInfo, err error) { + tinfo, err = getTypeInfo(v) + return +} + +// GetSingleIDOfName is a helper that returns single display ID for IDispatch name. +// +// This replaces the common pattern of attempting to get a single name from the list of available +// IDs. It gives the first ID, if it is available. +func (v *IDispatch) GetSingleIDOfName(name string) (displayID int32, err error) { + var displayIDs []int32 + displayIDs, err = v.GetIDsOfName([]string{name}) + if err != nil { + return + } + displayID = displayIDs[0] + return +} + +// InvokeWithOptionalArgs accepts arguments as an array, works like Invoke. +// +// Accepts name and will attempt to retrieve Display ID to pass to Invoke. +// +// Passing params as an array is a workaround that could be fixed in later versions of Go that +// prevent passing empty params. During testing it was discovered that this is an acceptable way of +// getting around not being able to pass params normally. +func (v *IDispatch) InvokeWithOptionalArgs(name string, dispatch int16, params []interface{}) (result *VARIANT, err error) { + displayID, err := v.GetSingleIDOfName(name) + if err != nil { + return + } + + if len(params) < 1 { + result, err = v.Invoke(displayID, dispatch) + } else { + result, err = v.Invoke(displayID, dispatch, params...) + } + + return +} + +// CallMethod invokes named function with arguments on object. +func (v *IDispatch) CallMethod(name string, params ...interface{}) (*VARIANT, error) { + return v.InvokeWithOptionalArgs(name, DISPATCH_METHOD, params) +} + +// GetProperty retrieves the property with the name with the ability to pass arguments. +// +// Most of the time you will not need to pass arguments as most objects do not allow for this +// feature. Or at least, should not allow for this feature. Some servers don't follow best practices +// and this is provided for those edge cases. +func (v *IDispatch) GetProperty(name string, params ...interface{}) (*VARIANT, error) { + return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYGET, params) +} + +// PutProperty attempts to mutate a property in the object. +func (v *IDispatch) PutProperty(name string, params ...interface{}) (*VARIANT, error) { + return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYPUT, params) +} diff --git a/vendor/github.com/go-ole/go-ole/idispatch_func.go b/vendor/github.com/go-ole/go-ole/idispatch_func.go new file mode 100644 index 00000000..b8fbbe31 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/idispatch_func.go @@ -0,0 +1,19 @@ +// +build !windows + +package ole + +func getIDsOfName(disp *IDispatch, names []string) ([]int32, error) { + return []int32{}, NewError(E_NOTIMPL) +} + +func getTypeInfoCount(disp *IDispatch) (uint32, error) { + return uint32(0), NewError(E_NOTIMPL) +} + +func getTypeInfo(disp *IDispatch) (*ITypeInfo, error) { + return nil, NewError(E_NOTIMPL) +} + +func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (*VARIANT, error) { + return nil, NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/idispatch_windows.go b/vendor/github.com/go-ole/go-ole/idispatch_windows.go new file mode 100644 index 00000000..b399f047 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/idispatch_windows.go @@ -0,0 +1,202 @@ +// +build windows + +package ole + +import ( + "math/big" + "syscall" + "time" + "unsafe" +) + +func getIDsOfName(disp *IDispatch, names []string) (dispid []int32, err error) { + wnames := make([]*uint16, len(names)) + for i := 0; i < len(names); i++ { + wnames[i] = syscall.StringToUTF16Ptr(names[i]) + } + dispid = make([]int32, len(names)) + namelen := uint32(len(names)) + hr, _, _ := syscall.Syscall6( + disp.VTable().GetIDsOfNames, + 6, + uintptr(unsafe.Pointer(disp)), + uintptr(unsafe.Pointer(IID_NULL)), + uintptr(unsafe.Pointer(&wnames[0])), + uintptr(namelen), + uintptr(GetUserDefaultLCID()), + uintptr(unsafe.Pointer(&dispid[0]))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func getTypeInfoCount(disp *IDispatch) (c uint32, err error) { + hr, _, _ := syscall.Syscall( + disp.VTable().GetTypeInfoCount, + 2, + uintptr(unsafe.Pointer(disp)), + uintptr(unsafe.Pointer(&c)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func getTypeInfo(disp *IDispatch) (tinfo *ITypeInfo, err error) { + hr, _, _ := syscall.Syscall( + disp.VTable().GetTypeInfo, + 3, + uintptr(unsafe.Pointer(disp)), + uintptr(GetUserDefaultLCID()), + uintptr(unsafe.Pointer(&tinfo))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) { + var dispparams DISPPARAMS + + if dispatch&DISPATCH_PROPERTYPUT != 0 { + dispnames := [1]int32{DISPID_PROPERTYPUT} + dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0])) + dispparams.cNamedArgs = 1 + } else if dispatch&DISPATCH_PROPERTYPUTREF != 0 { + dispnames := [1]int32{DISPID_PROPERTYPUT} + dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0])) + dispparams.cNamedArgs = 1 + } + var vargs []VARIANT + if len(params) > 0 { + vargs = make([]VARIANT, len(params)) + for i, v := range params { + //n := len(params)-i-1 + n := len(params) - i - 1 + VariantInit(&vargs[n]) + switch vv := v.(type) { + case bool: + if vv { + vargs[n] = NewVariant(VT_BOOL, 0xffff) + } else { + vargs[n] = NewVariant(VT_BOOL, 0) + } + case *bool: + vargs[n] = NewVariant(VT_BOOL|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*bool))))) + case uint8: + vargs[n] = NewVariant(VT_I1, int64(v.(uint8))) + case *uint8: + vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8))))) + case int8: + vargs[n] = NewVariant(VT_I1, int64(v.(int8))) + case *int8: + vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8))))) + case int16: + vargs[n] = NewVariant(VT_I2, int64(v.(int16))) + case *int16: + vargs[n] = NewVariant(VT_I2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int16))))) + case uint16: + vargs[n] = NewVariant(VT_UI2, int64(v.(uint16))) + case *uint16: + vargs[n] = NewVariant(VT_UI2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint16))))) + case int32: + vargs[n] = NewVariant(VT_I4, int64(v.(int32))) + case *int32: + vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int32))))) + case uint32: + vargs[n] = NewVariant(VT_UI4, int64(v.(uint32))) + case *uint32: + vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint32))))) + case int64: + vargs[n] = NewVariant(VT_I8, int64(v.(int64))) + case *int64: + vargs[n] = NewVariant(VT_I8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int64))))) + case uint64: + vargs[n] = NewVariant(VT_UI8, int64(uintptr(v.(uint64)))) + case *uint64: + vargs[n] = NewVariant(VT_UI8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint64))))) + case int: + vargs[n] = NewVariant(VT_I4, int64(v.(int))) + case *int: + vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int))))) + case uint: + vargs[n] = NewVariant(VT_UI4, int64(v.(uint))) + case *uint: + vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint))))) + case float32: + vargs[n] = NewVariant(VT_R4, *(*int64)(unsafe.Pointer(&vv))) + case *float32: + vargs[n] = NewVariant(VT_R4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float32))))) + case float64: + vargs[n] = NewVariant(VT_R8, *(*int64)(unsafe.Pointer(&vv))) + case *float64: + vargs[n] = NewVariant(VT_R8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float64))))) + case *big.Int: + vargs[n] = NewVariant(VT_DECIMAL, v.(*big.Int).Int64()) + case string: + vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(v.(string)))))) + case *string: + vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*string))))) + case time.Time: + s := vv.Format("2006-01-02 15:04:05") + vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(s))))) + case *time.Time: + s := vv.Format("2006-01-02 15:04:05") + vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(&s)))) + case *IDispatch: + vargs[n] = NewVariant(VT_DISPATCH, int64(uintptr(unsafe.Pointer(v.(*IDispatch))))) + case **IDispatch: + vargs[n] = NewVariant(VT_DISPATCH|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(**IDispatch))))) + case nil: + vargs[n] = NewVariant(VT_NULL, 0) + case *VARIANT: + vargs[n] = NewVariant(VT_VARIANT|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*VARIANT))))) + case []byte: + safeByteArray := safeArrayFromByteSlice(v.([]byte)) + vargs[n] = NewVariant(VT_ARRAY|VT_UI1, int64(uintptr(unsafe.Pointer(safeByteArray)))) + defer VariantClear(&vargs[n]) + case []string: + safeByteArray := safeArrayFromStringSlice(v.([]string)) + vargs[n] = NewVariant(VT_ARRAY|VT_BSTR, int64(uintptr(unsafe.Pointer(safeByteArray)))) + defer VariantClear(&vargs[n]) + default: + panic("unknown type") + } + } + dispparams.rgvarg = uintptr(unsafe.Pointer(&vargs[0])) + dispparams.cArgs = uint32(len(params)) + } + + result = new(VARIANT) + var excepInfo EXCEPINFO + VariantInit(result) + hr, _, _ := syscall.Syscall9( + disp.VTable().Invoke, + 9, + uintptr(unsafe.Pointer(disp)), + uintptr(dispid), + uintptr(unsafe.Pointer(IID_NULL)), + uintptr(GetUserDefaultLCID()), + uintptr(dispatch), + uintptr(unsafe.Pointer(&dispparams)), + uintptr(unsafe.Pointer(result)), + uintptr(unsafe.Pointer(&excepInfo)), + 0) + if hr != 0 { + excepInfo.renderStrings() + excepInfo.Clear() + err = NewErrorWithSubError(hr, excepInfo.description, excepInfo) + } + for i, varg := range vargs { + n := len(params) - i - 1 + if varg.VT == VT_BSTR && varg.Val != 0 { + SysFreeString(((*int16)(unsafe.Pointer(uintptr(varg.Val))))) + } + if varg.VT == (VT_BSTR|VT_BYREF) && varg.Val != 0 { + *(params[n].(*string)) = LpOleStrToString(*(**uint16)(unsafe.Pointer(uintptr(varg.Val)))) + } + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/ienumvariant.go b/vendor/github.com/go-ole/go-ole/ienumvariant.go new file mode 100644 index 00000000..24338975 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/ienumvariant.go @@ -0,0 +1,19 @@ +package ole + +import "unsafe" + +type IEnumVARIANT struct { + IUnknown +} + +type IEnumVARIANTVtbl struct { + IUnknownVtbl + Next uintptr + Skip uintptr + Reset uintptr + Clone uintptr +} + +func (v *IEnumVARIANT) VTable() *IEnumVARIANTVtbl { + return (*IEnumVARIANTVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/vendor/github.com/go-ole/go-ole/ienumvariant_func.go b/vendor/github.com/go-ole/go-ole/ienumvariant_func.go new file mode 100644 index 00000000..c1484819 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/ienumvariant_func.go @@ -0,0 +1,19 @@ +// +build !windows + +package ole + +func (enum *IEnumVARIANT) Clone() (*IEnumVARIANT, error) { + return nil, NewError(E_NOTIMPL) +} + +func (enum *IEnumVARIANT) Reset() error { + return NewError(E_NOTIMPL) +} + +func (enum *IEnumVARIANT) Skip(celt uint) error { + return NewError(E_NOTIMPL) +} + +func (enum *IEnumVARIANT) Next(celt uint) (VARIANT, uint, error) { + return NewVariant(VT_NULL, int64(0)), 0, NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/ienumvariant_windows.go b/vendor/github.com/go-ole/go-ole/ienumvariant_windows.go new file mode 100644 index 00000000..4781f3b8 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/ienumvariant_windows.go @@ -0,0 +1,63 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (enum *IEnumVARIANT) Clone() (cloned *IEnumVARIANT, err error) { + hr, _, _ := syscall.Syscall( + enum.VTable().Clone, + 2, + uintptr(unsafe.Pointer(enum)), + uintptr(unsafe.Pointer(&cloned)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (enum *IEnumVARIANT) Reset() (err error) { + hr, _, _ := syscall.Syscall( + enum.VTable().Reset, + 1, + uintptr(unsafe.Pointer(enum)), + 0, + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (enum *IEnumVARIANT) Skip(celt uint) (err error) { + hr, _, _ := syscall.Syscall( + enum.VTable().Skip, + 2, + uintptr(unsafe.Pointer(enum)), + uintptr(celt), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (enum *IEnumVARIANT) Next(celt uint) (array VARIANT, length uint, err error) { + hr, _, _ := syscall.Syscall6( + enum.VTable().Next, + 4, + uintptr(unsafe.Pointer(enum)), + uintptr(celt), + uintptr(unsafe.Pointer(&array)), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/iinspectable.go b/vendor/github.com/go-ole/go-ole/iinspectable.go new file mode 100644 index 00000000..f4a19e25 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iinspectable.go @@ -0,0 +1,18 @@ +package ole + +import "unsafe" + +type IInspectable struct { + IUnknown +} + +type IInspectableVtbl struct { + IUnknownVtbl + GetIIds uintptr + GetRuntimeClassName uintptr + GetTrustLevel uintptr +} + +func (v *IInspectable) VTable() *IInspectableVtbl { + return (*IInspectableVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/vendor/github.com/go-ole/go-ole/iinspectable_func.go b/vendor/github.com/go-ole/go-ole/iinspectable_func.go new file mode 100644 index 00000000..348829bf --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iinspectable_func.go @@ -0,0 +1,15 @@ +// +build !windows + +package ole + +func (v *IInspectable) GetIids() ([]*GUID, error) { + return []*GUID{}, NewError(E_NOTIMPL) +} + +func (v *IInspectable) GetRuntimeClassName() (string, error) { + return "", NewError(E_NOTIMPL) +} + +func (v *IInspectable) GetTrustLevel() (uint32, error) { + return uint32(0), NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/iinspectable_windows.go b/vendor/github.com/go-ole/go-ole/iinspectable_windows.go new file mode 100644 index 00000000..4519a4aa --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iinspectable_windows.go @@ -0,0 +1,72 @@ +// +build windows + +package ole + +import ( + "bytes" + "encoding/binary" + "reflect" + "syscall" + "unsafe" +) + +func (v *IInspectable) GetIids() (iids []*GUID, err error) { + var count uint32 + var array uintptr + hr, _, _ := syscall.Syscall( + v.VTable().GetIIds, + 3, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&count)), + uintptr(unsafe.Pointer(&array))) + if hr != 0 { + err = NewError(hr) + return + } + defer CoTaskMemFree(array) + + iids = make([]*GUID, count) + byteCount := count * uint32(unsafe.Sizeof(GUID{})) + slicehdr := reflect.SliceHeader{Data: array, Len: int(byteCount), Cap: int(byteCount)} + byteSlice := *(*[]byte)(unsafe.Pointer(&slicehdr)) + reader := bytes.NewReader(byteSlice) + for i := range iids { + guid := GUID{} + err = binary.Read(reader, binary.LittleEndian, &guid) + if err != nil { + return + } + iids[i] = &guid + } + return +} + +func (v *IInspectable) GetRuntimeClassName() (s string, err error) { + var hstring HString + hr, _, _ := syscall.Syscall( + v.VTable().GetRuntimeClassName, + 2, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&hstring)), + 0) + if hr != 0 { + err = NewError(hr) + return + } + s = hstring.String() + DeleteHString(hstring) + return +} + +func (v *IInspectable) GetTrustLevel() (level uint32, err error) { + hr, _, _ := syscall.Syscall( + v.VTable().GetTrustLevel, + 2, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&level)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/iprovideclassinfo.go b/vendor/github.com/go-ole/go-ole/iprovideclassinfo.go new file mode 100644 index 00000000..25f3a6f2 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iprovideclassinfo.go @@ -0,0 +1,21 @@ +package ole + +import "unsafe" + +type IProvideClassInfo struct { + IUnknown +} + +type IProvideClassInfoVtbl struct { + IUnknownVtbl + GetClassInfo uintptr +} + +func (v *IProvideClassInfo) VTable() *IProvideClassInfoVtbl { + return (*IProvideClassInfoVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *IProvideClassInfo) GetClassInfo() (cinfo *ITypeInfo, err error) { + cinfo, err = getClassInfo(v) + return +} diff --git a/vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go b/vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go new file mode 100644 index 00000000..7e3cb63e --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go @@ -0,0 +1,7 @@ +// +build !windows + +package ole + +func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) { + return nil, NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go b/vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go new file mode 100644 index 00000000..2ad01639 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go @@ -0,0 +1,21 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) { + hr, _, _ := syscall.Syscall( + disp.VTable().GetClassInfo, + 2, + uintptr(unsafe.Pointer(disp)), + uintptr(unsafe.Pointer(&tinfo)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/itypeinfo.go b/vendor/github.com/go-ole/go-ole/itypeinfo.go new file mode 100644 index 00000000..dd3c5e21 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/itypeinfo.go @@ -0,0 +1,34 @@ +package ole + +import "unsafe" + +type ITypeInfo struct { + IUnknown +} + +type ITypeInfoVtbl struct { + IUnknownVtbl + GetTypeAttr uintptr + GetTypeComp uintptr + GetFuncDesc uintptr + GetVarDesc uintptr + GetNames uintptr + GetRefTypeOfImplType uintptr + GetImplTypeFlags uintptr + GetIDsOfNames uintptr + Invoke uintptr + GetDocumentation uintptr + GetDllEntry uintptr + GetRefTypeInfo uintptr + AddressOfMember uintptr + CreateInstance uintptr + GetMops uintptr + GetContainingTypeLib uintptr + ReleaseTypeAttr uintptr + ReleaseFuncDesc uintptr + ReleaseVarDesc uintptr +} + +func (v *ITypeInfo) VTable() *ITypeInfoVtbl { + return (*ITypeInfoVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/vendor/github.com/go-ole/go-ole/itypeinfo_func.go b/vendor/github.com/go-ole/go-ole/itypeinfo_func.go new file mode 100644 index 00000000..8364a659 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/itypeinfo_func.go @@ -0,0 +1,7 @@ +// +build !windows + +package ole + +func (v *ITypeInfo) GetTypeAttr() (*TYPEATTR, error) { + return nil, NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/itypeinfo_windows.go b/vendor/github.com/go-ole/go-ole/itypeinfo_windows.go new file mode 100644 index 00000000..54782b3d --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/itypeinfo_windows.go @@ -0,0 +1,21 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (v *ITypeInfo) GetTypeAttr() (tattr *TYPEATTR, err error) { + hr, _, _ := syscall.Syscall( + uintptr(v.VTable().GetTypeAttr), + 2, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&tattr)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/iunknown.go b/vendor/github.com/go-ole/go-ole/iunknown.go new file mode 100644 index 00000000..108f28ea --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iunknown.go @@ -0,0 +1,57 @@ +package ole + +import "unsafe" + +type IUnknown struct { + RawVTable *interface{} +} + +type IUnknownVtbl struct { + QueryInterface uintptr + AddRef uintptr + Release uintptr +} + +type UnknownLike interface { + QueryInterface(iid *GUID) (disp *IDispatch, err error) + AddRef() int32 + Release() int32 +} + +func (v *IUnknown) VTable() *IUnknownVtbl { + return (*IUnknownVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *IUnknown) PutQueryInterface(interfaceID *GUID, obj interface{}) error { + return reflectQueryInterface(v, v.VTable().QueryInterface, interfaceID, obj) +} + +func (v *IUnknown) IDispatch(interfaceID *GUID) (dispatch *IDispatch, err error) { + err = v.PutQueryInterface(interfaceID, &dispatch) + return +} + +func (v *IUnknown) IEnumVARIANT(interfaceID *GUID) (enum *IEnumVARIANT, err error) { + err = v.PutQueryInterface(interfaceID, &enum) + return +} + +func (v *IUnknown) QueryInterface(iid *GUID) (*IDispatch, error) { + return queryInterface(v, iid) +} + +func (v *IUnknown) MustQueryInterface(iid *GUID) (disp *IDispatch) { + unk, err := queryInterface(v, iid) + if err != nil { + panic(err) + } + return unk +} + +func (v *IUnknown) AddRef() int32 { + return addRef(v) +} + +func (v *IUnknown) Release() int32 { + return release(v) +} diff --git a/vendor/github.com/go-ole/go-ole/iunknown_func.go b/vendor/github.com/go-ole/go-ole/iunknown_func.go new file mode 100644 index 00000000..d0a62cfd --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iunknown_func.go @@ -0,0 +1,19 @@ +// +build !windows + +package ole + +func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) { + return NewError(E_NOTIMPL) +} + +func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) { + return nil, NewError(E_NOTIMPL) +} + +func addRef(unk *IUnknown) int32 { + return 0 +} + +func release(unk *IUnknown) int32 { + return 0 +} diff --git a/vendor/github.com/go-ole/go-ole/iunknown_windows.go b/vendor/github.com/go-ole/go-ole/iunknown_windows.go new file mode 100644 index 00000000..ede5bb8c --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/iunknown_windows.go @@ -0,0 +1,58 @@ +// +build windows + +package ole + +import ( + "reflect" + "syscall" + "unsafe" +) + +func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) { + selfValue := reflect.ValueOf(self).Elem() + objValue := reflect.ValueOf(obj).Elem() + + hr, _, _ := syscall.Syscall( + method, + 3, + selfValue.UnsafeAddr(), + uintptr(unsafe.Pointer(interfaceID)), + objValue.Addr().Pointer()) + if hr != 0 { + err = NewError(hr) + } + return +} + +func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) { + hr, _, _ := syscall.Syscall( + unk.VTable().QueryInterface, + 3, + uintptr(unsafe.Pointer(unk)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&disp))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func addRef(unk *IUnknown) int32 { + ret, _, _ := syscall.Syscall( + unk.VTable().AddRef, + 1, + uintptr(unsafe.Pointer(unk)), + 0, + 0) + return int32(ret) +} + +func release(unk *IUnknown) int32 { + ret, _, _ := syscall.Syscall( + unk.VTable().Release, + 1, + uintptr(unsafe.Pointer(unk)), + 0, + 0) + return int32(ret) +} diff --git a/vendor/github.com/go-ole/go-ole/ole.go b/vendor/github.com/go-ole/go-ole/ole.go new file mode 100644 index 00000000..dbd132bb --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/ole.go @@ -0,0 +1,190 @@ +package ole + +import ( + "fmt" + "strings" + "unsafe" +) + +// DISPPARAMS are the arguments that passed to methods or property. +type DISPPARAMS struct { + rgvarg uintptr + rgdispidNamedArgs uintptr + cArgs uint32 + cNamedArgs uint32 +} + +// EXCEPINFO defines exception info. +type EXCEPINFO struct { + wCode uint16 + wReserved uint16 + bstrSource *uint16 + bstrDescription *uint16 + bstrHelpFile *uint16 + dwHelpContext uint32 + pvReserved uintptr + pfnDeferredFillIn uintptr + scode uint32 + + // Go-specific part. Don't move upper cos it'll break structure layout for native code. + rendered bool + source string + description string + helpFile string +} + +// renderStrings translates BSTR strings to Go ones so `.Error` and `.String` +// could be safely called after `.Clear`. We need this when we can't rely on +// a caller to call `.Clear`. +func (e *EXCEPINFO) renderStrings() { + e.rendered = true + if e.bstrSource == nil { + e.source = "" + } else { + e.source = BstrToString(e.bstrSource) + } + if e.bstrDescription == nil { + e.description = "" + } else { + e.description = BstrToString(e.bstrDescription) + } + if e.bstrHelpFile == nil { + e.helpFile = "" + } else { + e.helpFile = BstrToString(e.bstrHelpFile) + } +} + +// Clear frees BSTR strings inside an EXCEPINFO and set it to NULL. +func (e *EXCEPINFO) Clear() { + freeBSTR := func(s *uint16) { + // SysFreeString don't return errors and is safe for call's on NULL. + // https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysfreestring + _ = SysFreeString((*int16)(unsafe.Pointer(s))) + } + + if e.bstrSource != nil { + freeBSTR(e.bstrSource) + e.bstrSource = nil + } + if e.bstrDescription != nil { + freeBSTR(e.bstrDescription) + e.bstrDescription = nil + } + if e.bstrHelpFile != nil { + freeBSTR(e.bstrHelpFile) + e.bstrHelpFile = nil + } +} + +// WCode return wCode in EXCEPINFO. +func (e EXCEPINFO) WCode() uint16 { + return e.wCode +} + +// SCODE return scode in EXCEPINFO. +func (e EXCEPINFO) SCODE() uint32 { + return e.scode +} + +// String convert EXCEPINFO to string. +func (e EXCEPINFO) String() string { + if !e.rendered { + e.renderStrings() + } + return fmt.Sprintf( + "wCode: %#x, bstrSource: %v, bstrDescription: %v, bstrHelpFile: %v, dwHelpContext: %#x, scode: %#x", + e.wCode, e.source, e.description, e.helpFile, e.dwHelpContext, e.scode, + ) +} + +// Error implements error interface and returns error string. +func (e EXCEPINFO) Error() string { + if !e.rendered { + e.renderStrings() + } + + if e.description != "" { + return strings.TrimSpace(e.description) + } + + code := e.scode + if e.wCode != 0 { + code = uint32(e.wCode) + } + return fmt.Sprintf("%v: %#x", e.source, code) +} + +// PARAMDATA defines parameter data type. +type PARAMDATA struct { + Name *int16 + Vt uint16 +} + +// METHODDATA defines method info. +type METHODDATA struct { + Name *uint16 + Data *PARAMDATA + Dispid int32 + Meth uint32 + CC int32 + CArgs uint32 + Flags uint16 + VtReturn uint32 +} + +// INTERFACEDATA defines interface info. +type INTERFACEDATA struct { + MethodData *METHODDATA + CMembers uint32 +} + +// Point is 2D vector type. +type Point struct { + X int32 + Y int32 +} + +// Msg is message between processes. +type Msg struct { + Hwnd uint32 + Message uint32 + Wparam int32 + Lparam int32 + Time uint32 + Pt Point +} + +// TYPEDESC defines data type. +type TYPEDESC struct { + Hreftype uint32 + VT uint16 +} + +// IDLDESC defines IDL info. +type IDLDESC struct { + DwReserved uint32 + WIDLFlags uint16 +} + +// TYPEATTR defines type info. +type TYPEATTR struct { + Guid GUID + Lcid uint32 + dwReserved uint32 + MemidConstructor int32 + MemidDestructor int32 + LpstrSchema *uint16 + CbSizeInstance uint32 + Typekind int32 + CFuncs uint16 + CVars uint16 + CImplTypes uint16 + CbSizeVft uint16 + CbAlignment uint16 + WTypeFlags uint16 + WMajorVerNum uint16 + WMinorVerNum uint16 + TdescAlias TYPEDESC + IdldescType IDLDESC +} diff --git a/vendor/github.com/go-ole/go-ole/oleutil/connection.go b/vendor/github.com/go-ole/go-ole/oleutil/connection.go new file mode 100644 index 00000000..60df73cd --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/oleutil/connection.go @@ -0,0 +1,100 @@ +// +build windows + +package oleutil + +import ( + "reflect" + "unsafe" + + ole "github.com/go-ole/go-ole" +) + +type stdDispatch struct { + lpVtbl *stdDispatchVtbl + ref int32 + iid *ole.GUID + iface interface{} + funcMap map[string]int32 +} + +type stdDispatchVtbl struct { + pQueryInterface uintptr + pAddRef uintptr + pRelease uintptr + pGetTypeInfoCount uintptr + pGetTypeInfo uintptr + pGetIDsOfNames uintptr + pInvoke uintptr +} + +func dispQueryInterface(this *ole.IUnknown, iid *ole.GUID, punk **ole.IUnknown) uint32 { + pthis := (*stdDispatch)(unsafe.Pointer(this)) + *punk = nil + if ole.IsEqualGUID(iid, ole.IID_IUnknown) || + ole.IsEqualGUID(iid, ole.IID_IDispatch) { + dispAddRef(this) + *punk = this + return ole.S_OK + } + if ole.IsEqualGUID(iid, pthis.iid) { + dispAddRef(this) + *punk = this + return ole.S_OK + } + return ole.E_NOINTERFACE +} + +func dispAddRef(this *ole.IUnknown) int32 { + pthis := (*stdDispatch)(unsafe.Pointer(this)) + pthis.ref++ + return pthis.ref +} + +func dispRelease(this *ole.IUnknown) int32 { + pthis := (*stdDispatch)(unsafe.Pointer(this)) + pthis.ref-- + return pthis.ref +} + +func dispGetIDsOfNames(this *ole.IUnknown, iid *ole.GUID, wnames []*uint16, namelen int, lcid int, pdisp []int32) uintptr { + pthis := (*stdDispatch)(unsafe.Pointer(this)) + names := make([]string, len(wnames)) + for i := 0; i < len(names); i++ { + names[i] = ole.LpOleStrToString(wnames[i]) + } + for n := 0; n < namelen; n++ { + if id, ok := pthis.funcMap[names[n]]; ok { + pdisp[n] = id + } + } + return ole.S_OK +} + +func dispGetTypeInfoCount(pcount *int) uintptr { + if pcount != nil { + *pcount = 0 + } + return ole.S_OK +} + +func dispGetTypeInfo(ptypeif *uintptr) uintptr { + return ole.E_NOTIMPL +} + +func dispInvoke(this *ole.IDispatch, dispid int32, riid *ole.GUID, lcid int, flags int16, dispparams *ole.DISPPARAMS, result *ole.VARIANT, pexcepinfo *ole.EXCEPINFO, nerr *uint) uintptr { + pthis := (*stdDispatch)(unsafe.Pointer(this)) + found := "" + for name, id := range pthis.funcMap { + if id == dispid { + found = name + } + } + if found != "" { + rv := reflect.ValueOf(pthis.iface).Elem() + rm := rv.MethodByName(found) + rr := rm.Call([]reflect.Value{}) + println(len(rr)) + return ole.S_OK + } + return ole.E_NOTIMPL +} diff --git a/vendor/github.com/go-ole/go-ole/oleutil/connection_func.go b/vendor/github.com/go-ole/go-ole/oleutil/connection_func.go new file mode 100644 index 00000000..8818fb82 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/oleutil/connection_func.go @@ -0,0 +1,10 @@ +// +build !windows + +package oleutil + +import ole "github.com/go-ole/go-ole" + +// ConnectObject creates a connection point between two services for communication. +func ConnectObject(disp *ole.IDispatch, iid *ole.GUID, idisp interface{}) (uint32, error) { + return 0, ole.NewError(ole.E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/oleutil/connection_windows.go b/vendor/github.com/go-ole/go-ole/oleutil/connection_windows.go new file mode 100644 index 00000000..ab9c0d8d --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/oleutil/connection_windows.go @@ -0,0 +1,58 @@ +// +build windows + +package oleutil + +import ( + "reflect" + "syscall" + "unsafe" + + ole "github.com/go-ole/go-ole" +) + +// ConnectObject creates a connection point between two services for communication. +func ConnectObject(disp *ole.IDispatch, iid *ole.GUID, idisp interface{}) (cookie uint32, err error) { + unknown, err := disp.QueryInterface(ole.IID_IConnectionPointContainer) + if err != nil { + return + } + + container := (*ole.IConnectionPointContainer)(unsafe.Pointer(unknown)) + var point *ole.IConnectionPoint + err = container.FindConnectionPoint(iid, &point) + if err != nil { + return + } + if edisp, ok := idisp.(*ole.IUnknown); ok { + cookie, err = point.Advise(edisp) + container.Release() + if err != nil { + return + } + } + rv := reflect.ValueOf(disp).Elem() + if rv.Type().Kind() == reflect.Struct { + dest := &stdDispatch{} + dest.lpVtbl = &stdDispatchVtbl{} + dest.lpVtbl.pQueryInterface = syscall.NewCallback(dispQueryInterface) + dest.lpVtbl.pAddRef = syscall.NewCallback(dispAddRef) + dest.lpVtbl.pRelease = syscall.NewCallback(dispRelease) + dest.lpVtbl.pGetTypeInfoCount = syscall.NewCallback(dispGetTypeInfoCount) + dest.lpVtbl.pGetTypeInfo = syscall.NewCallback(dispGetTypeInfo) + dest.lpVtbl.pGetIDsOfNames = syscall.NewCallback(dispGetIDsOfNames) + dest.lpVtbl.pInvoke = syscall.NewCallback(dispInvoke) + dest.iface = disp + dest.iid = iid + cookie, err = point.Advise((*ole.IUnknown)(unsafe.Pointer(dest))) + container.Release() + if err != nil { + point.Release() + return + } + return + } + + container.Release() + + return 0, ole.NewError(ole.E_INVALIDARG) +} diff --git a/vendor/github.com/go-ole/go-ole/oleutil/go-get.go b/vendor/github.com/go-ole/go-ole/oleutil/go-get.go new file mode 100644 index 00000000..58347628 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/oleutil/go-get.go @@ -0,0 +1,6 @@ +// This file is here so go get succeeds as without it errors with: +// no buildable Go source files in ... +// +// +build !windows + +package oleutil diff --git a/vendor/github.com/go-ole/go-ole/oleutil/oleutil.go b/vendor/github.com/go-ole/go-ole/oleutil/oleutil.go new file mode 100644 index 00000000..f7803c1e --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/oleutil/oleutil.go @@ -0,0 +1,127 @@ +package oleutil + +import ole "github.com/go-ole/go-ole" + +// ClassIDFrom retrieves class ID whether given is program ID or application string. +func ClassIDFrom(programID string) (classID *ole.GUID, err error) { + return ole.ClassIDFrom(programID) +} + +// CreateObject creates object from programID based on interface type. +// +// Only supports IUnknown. +// +// Program ID can be either program ID or application string. +func CreateObject(programID string) (unknown *ole.IUnknown, err error) { + classID, err := ole.ClassIDFrom(programID) + if err != nil { + return + } + + unknown, err = ole.CreateInstance(classID, ole.IID_IUnknown) + if err != nil { + return + } + + return +} + +// GetActiveObject retrieves active object for program ID and interface ID based +// on interface type. +// +// Only supports IUnknown. +// +// Program ID can be either program ID or application string. +func GetActiveObject(programID string) (unknown *ole.IUnknown, err error) { + classID, err := ole.ClassIDFrom(programID) + if err != nil { + return + } + + unknown, err = ole.GetActiveObject(classID, ole.IID_IUnknown) + if err != nil { + return + } + + return +} + +// CallMethod calls method on IDispatch with parameters. +func CallMethod(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) { + return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_METHOD, params) +} + +// MustCallMethod calls method on IDispatch with parameters or panics. +func MustCallMethod(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) { + r, err := CallMethod(disp, name, params...) + if err != nil { + panic(err.Error()) + } + return r +} + +// GetProperty retrieves property from IDispatch. +func GetProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) { + return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_PROPERTYGET, params) +} + +// MustGetProperty retrieves property from IDispatch or panics. +func MustGetProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) { + r, err := GetProperty(disp, name, params...) + if err != nil { + panic(err.Error()) + } + return r +} + +// PutProperty mutates property. +func PutProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) { + return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_PROPERTYPUT, params) +} + +// MustPutProperty mutates property or panics. +func MustPutProperty(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) { + r, err := PutProperty(disp, name, params...) + if err != nil { + panic(err.Error()) + } + return r +} + +// PutPropertyRef mutates property reference. +func PutPropertyRef(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) { + return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_PROPERTYPUTREF, params) +} + +// MustPutPropertyRef mutates property reference or panics. +func MustPutPropertyRef(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) { + r, err := PutPropertyRef(disp, name, params...) + if err != nil { + panic(err.Error()) + } + return r +} + +func ForEach(disp *ole.IDispatch, f func(v *ole.VARIANT) error) error { + newEnum, err := disp.GetProperty("_NewEnum") + if err != nil { + return err + } + defer newEnum.Clear() + + enum, err := newEnum.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant) + if err != nil { + return err + } + defer enum.Release() + + for item, length, err := enum.Next(1); length > 0; item, length, err = enum.Next(1) { + if err != nil { + return err + } + if ferr := f(&item); ferr != nil { + return ferr + } + } + return nil +} diff --git a/vendor/github.com/go-ole/go-ole/safearray.go b/vendor/github.com/go-ole/go-ole/safearray.go new file mode 100644 index 00000000..a5201b56 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/safearray.go @@ -0,0 +1,27 @@ +// Package is meant to retrieve and process safe array data returned from COM. + +package ole + +// SafeArrayBound defines the SafeArray boundaries. +type SafeArrayBound struct { + Elements uint32 + LowerBound int32 +} + +// SafeArray is how COM handles arrays. +type SafeArray struct { + Dimensions uint16 + FeaturesFlag uint16 + ElementsSize uint32 + LocksAmount uint32 + Data uint32 + Bounds [16]byte +} + +// SAFEARRAY is obsolete, exists for backwards compatibility. +// Use SafeArray +type SAFEARRAY SafeArray + +// SAFEARRAYBOUND is obsolete, exists for backwards compatibility. +// Use SafeArrayBound +type SAFEARRAYBOUND SafeArrayBound diff --git a/vendor/github.com/go-ole/go-ole/safearray_func.go b/vendor/github.com/go-ole/go-ole/safearray_func.go new file mode 100644 index 00000000..0dee670c --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/safearray_func.go @@ -0,0 +1,211 @@ +// +build !windows + +package ole + +import ( + "unsafe" +) + +// safeArrayAccessData returns raw array pointer. +// +// AKA: SafeArrayAccessData in Windows API. +func safeArrayAccessData(safearray *SafeArray) (uintptr, error) { + return uintptr(0), NewError(E_NOTIMPL) +} + +// safeArrayUnaccessData releases raw array. +// +// AKA: SafeArrayUnaccessData in Windows API. +func safeArrayUnaccessData(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayAllocData allocates SafeArray. +// +// AKA: SafeArrayAllocData in Windows API. +func safeArrayAllocData(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayAllocDescriptor allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptor in Windows API. +func safeArrayAllocDescriptor(dimensions uint32) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayAllocDescriptorEx allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptorEx in Windows API. +func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCopy returns copy of SafeArray. +// +// AKA: SafeArrayCopy in Windows API. +func safeArrayCopy(original *SafeArray) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCopyData duplicates SafeArray into another SafeArray object. +// +// AKA: SafeArrayCopyData in Windows API. +func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayCreate creates SafeArray. +// +// AKA: SafeArrayCreate in Windows API. +func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCreateEx creates SafeArray. +// +// AKA: SafeArrayCreateEx in Windows API. +func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCreateVector creates SafeArray. +// +// AKA: SafeArrayCreateVector in Windows API. +func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCreateVectorEx creates SafeArray. +// +// AKA: SafeArrayCreateVectorEx in Windows API. +func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayDestroy destroys SafeArray object. +// +// AKA: SafeArrayDestroy in Windows API. +func safeArrayDestroy(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayDestroyData destroys SafeArray object. +// +// AKA: SafeArrayDestroyData in Windows API. +func safeArrayDestroyData(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayDestroyDescriptor destroys SafeArray object. +// +// AKA: SafeArrayDestroyDescriptor in Windows API. +func safeArrayDestroyDescriptor(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayGetDim is the amount of dimensions in the SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetDim in Windows API. +func safeArrayGetDim(safearray *SafeArray) (*uint32, error) { + u := uint32(0) + return &u, NewError(E_NOTIMPL) +} + +// safeArrayGetElementSize is the element size in bytes. +// +// AKA: SafeArrayGetElemsize in Windows API. +func safeArrayGetElementSize(safearray *SafeArray) (*uint32, error) { + u := uint32(0) + return &u, NewError(E_NOTIMPL) +} + +// safeArrayGetElement retrieves element at given index. +func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error { + return NewError(E_NOTIMPL) +} + +// safeArrayGetElement retrieves element at given index and converts to string. +func safeArrayGetElementString(safearray *SafeArray, index int32) (string, error) { + return "", NewError(E_NOTIMPL) +} + +// safeArrayGetIID is the InterfaceID of the elements in the SafeArray. +// +// AKA: SafeArrayGetIID in Windows API. +func safeArrayGetIID(safearray *SafeArray) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayGetLBound returns lower bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetLBound in Windows API. +func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (int32, error) { + return int32(0), NewError(E_NOTIMPL) +} + +// safeArrayGetUBound returns upper bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetUBound in Windows API. +func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (int32, error) { + return int32(0), NewError(E_NOTIMPL) +} + +// safeArrayGetVartype returns data type of SafeArray. +// +// AKA: SafeArrayGetVartype in Windows API. +func safeArrayGetVartype(safearray *SafeArray) (uint16, error) { + return uint16(0), NewError(E_NOTIMPL) +} + +// safeArrayLock locks SafeArray for reading to modify SafeArray. +// +// This must be called during some calls to ensure that another process does not +// read or write to the SafeArray during editing. +// +// AKA: SafeArrayLock in Windows API. +func safeArrayLock(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayUnlock unlocks SafeArray for reading. +// +// AKA: SafeArrayUnlock in Windows API. +func safeArrayUnlock(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayPutElement stores the data element at the specified location in the +// array. +// +// AKA: SafeArrayPutElement in Windows API. +func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) error { + return NewError(E_NOTIMPL) +} + +// safeArrayGetRecordInfo accesses IRecordInfo info for custom types. +// +// AKA: SafeArrayGetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArrayGetRecordInfo(safearray *SafeArray) (interface{}, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArraySetRecordInfo mutates IRecordInfo info for custom types. +// +// AKA: SafeArraySetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) error { + return NewError(E_NOTIMPL) +} diff --git a/vendor/github.com/go-ole/go-ole/safearray_windows.go b/vendor/github.com/go-ole/go-ole/safearray_windows.go new file mode 100644 index 00000000..0c1b3a10 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/safearray_windows.go @@ -0,0 +1,337 @@ +// +build windows + +package ole + +import ( + "unsafe" +) + +var ( + procSafeArrayAccessData = modoleaut32.NewProc("SafeArrayAccessData") + procSafeArrayAllocData = modoleaut32.NewProc("SafeArrayAllocData") + procSafeArrayAllocDescriptor = modoleaut32.NewProc("SafeArrayAllocDescriptor") + procSafeArrayAllocDescriptorEx = modoleaut32.NewProc("SafeArrayAllocDescriptorEx") + procSafeArrayCopy = modoleaut32.NewProc("SafeArrayCopy") + procSafeArrayCopyData = modoleaut32.NewProc("SafeArrayCopyData") + procSafeArrayCreate = modoleaut32.NewProc("SafeArrayCreate") + procSafeArrayCreateEx = modoleaut32.NewProc("SafeArrayCreateEx") + procSafeArrayCreateVector = modoleaut32.NewProc("SafeArrayCreateVector") + procSafeArrayCreateVectorEx = modoleaut32.NewProc("SafeArrayCreateVectorEx") + procSafeArrayDestroy = modoleaut32.NewProc("SafeArrayDestroy") + procSafeArrayDestroyData = modoleaut32.NewProc("SafeArrayDestroyData") + procSafeArrayDestroyDescriptor = modoleaut32.NewProc("SafeArrayDestroyDescriptor") + procSafeArrayGetDim = modoleaut32.NewProc("SafeArrayGetDim") + procSafeArrayGetElement = modoleaut32.NewProc("SafeArrayGetElement") + procSafeArrayGetElemsize = modoleaut32.NewProc("SafeArrayGetElemsize") + procSafeArrayGetIID = modoleaut32.NewProc("SafeArrayGetIID") + procSafeArrayGetLBound = modoleaut32.NewProc("SafeArrayGetLBound") + procSafeArrayGetUBound = modoleaut32.NewProc("SafeArrayGetUBound") + procSafeArrayGetVartype = modoleaut32.NewProc("SafeArrayGetVartype") + procSafeArrayLock = modoleaut32.NewProc("SafeArrayLock") + procSafeArrayPtrOfIndex = modoleaut32.NewProc("SafeArrayPtrOfIndex") + procSafeArrayUnaccessData = modoleaut32.NewProc("SafeArrayUnaccessData") + procSafeArrayUnlock = modoleaut32.NewProc("SafeArrayUnlock") + procSafeArrayPutElement = modoleaut32.NewProc("SafeArrayPutElement") + //procSafeArrayRedim = modoleaut32.NewProc("SafeArrayRedim") // TODO + //procSafeArraySetIID = modoleaut32.NewProc("SafeArraySetIID") // TODO + procSafeArrayGetRecordInfo = modoleaut32.NewProc("SafeArrayGetRecordInfo") + procSafeArraySetRecordInfo = modoleaut32.NewProc("SafeArraySetRecordInfo") +) + +// safeArrayAccessData returns raw array pointer. +// +// AKA: SafeArrayAccessData in Windows API. +// Todo: Test +func safeArrayAccessData(safearray *SafeArray) (element uintptr, err error) { + err = convertHresultToError( + procSafeArrayAccessData.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&element)))) + return +} + +// safeArrayUnaccessData releases raw array. +// +// AKA: SafeArrayUnaccessData in Windows API. +func safeArrayUnaccessData(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayUnaccessData.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayAllocData allocates SafeArray. +// +// AKA: SafeArrayAllocData in Windows API. +func safeArrayAllocData(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayAllocData.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayAllocDescriptor allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptor in Windows API. +func safeArrayAllocDescriptor(dimensions uint32) (safearray *SafeArray, err error) { + err = convertHresultToError( + procSafeArrayAllocDescriptor.Call(uintptr(dimensions), uintptr(unsafe.Pointer(&safearray)))) + return +} + +// safeArrayAllocDescriptorEx allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptorEx in Windows API. +func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (safearray *SafeArray, err error) { + err = convertHresultToError( + procSafeArrayAllocDescriptorEx.Call( + uintptr(variantType), + uintptr(dimensions), + uintptr(unsafe.Pointer(&safearray)))) + return +} + +// safeArrayCopy returns copy of SafeArray. +// +// AKA: SafeArrayCopy in Windows API. +func safeArrayCopy(original *SafeArray) (safearray *SafeArray, err error) { + err = convertHresultToError( + procSafeArrayCopy.Call( + uintptr(unsafe.Pointer(original)), + uintptr(unsafe.Pointer(&safearray)))) + return +} + +// safeArrayCopyData duplicates SafeArray into another SafeArray object. +// +// AKA: SafeArrayCopyData in Windows API. +func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) (err error) { + err = convertHresultToError( + procSafeArrayCopyData.Call( + uintptr(unsafe.Pointer(original)), + uintptr(unsafe.Pointer(duplicate)))) + return +} + +// safeArrayCreate creates SafeArray. +// +// AKA: SafeArrayCreate in Windows API. +func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreate.Call( + uintptr(variantType), + uintptr(dimensions), + uintptr(unsafe.Pointer(bounds))) + safearray = (*SafeArray)(unsafe.Pointer(&sa)) + return +} + +// safeArrayCreateEx creates SafeArray. +// +// AKA: SafeArrayCreateEx in Windows API. +func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreateEx.Call( + uintptr(variantType), + uintptr(dimensions), + uintptr(unsafe.Pointer(bounds)), + extra) + safearray = (*SafeArray)(unsafe.Pointer(sa)) + return +} + +// safeArrayCreateVector creates SafeArray. +// +// AKA: SafeArrayCreateVector in Windows API. +func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreateVector.Call( + uintptr(variantType), + uintptr(lowerBound), + uintptr(length)) + safearray = (*SafeArray)(unsafe.Pointer(sa)) + return +} + +// safeArrayCreateVectorEx creates SafeArray. +// +// AKA: SafeArrayCreateVectorEx in Windows API. +func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreateVectorEx.Call( + uintptr(variantType), + uintptr(lowerBound), + uintptr(length), + extra) + safearray = (*SafeArray)(unsafe.Pointer(sa)) + return +} + +// safeArrayDestroy destroys SafeArray object. +// +// AKA: SafeArrayDestroy in Windows API. +func safeArrayDestroy(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayDestroy.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayDestroyData destroys SafeArray object. +// +// AKA: SafeArrayDestroyData in Windows API. +func safeArrayDestroyData(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayDestroyData.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayDestroyDescriptor destroys SafeArray object. +// +// AKA: SafeArrayDestroyDescriptor in Windows API. +func safeArrayDestroyDescriptor(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayDestroyDescriptor.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayGetDim is the amount of dimensions in the SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetDim in Windows API. +func safeArrayGetDim(safearray *SafeArray) (dimensions *uint32, err error) { + l, _, err := procSafeArrayGetDim.Call(uintptr(unsafe.Pointer(safearray))) + dimensions = (*uint32)(unsafe.Pointer(l)) + return +} + +// safeArrayGetElementSize is the element size in bytes. +// +// AKA: SafeArrayGetElemsize in Windows API. +func safeArrayGetElementSize(safearray *SafeArray) (length *uint32, err error) { + l, _, err := procSafeArrayGetElemsize.Call(uintptr(unsafe.Pointer(safearray))) + length = (*uint32)(unsafe.Pointer(l)) + return +} + +// safeArrayGetElement retrieves element at given index. +func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error { + return convertHresultToError( + procSafeArrayGetElement.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&index)), + uintptr(pv))) +} + +// safeArrayGetElementString retrieves element at given index and converts to string. +func safeArrayGetElementString(safearray *SafeArray, index int32) (str string, err error) { + var element *int16 + err = convertHresultToError( + procSafeArrayGetElement.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&index)), + uintptr(unsafe.Pointer(&element)))) + str = BstrToString(*(**uint16)(unsafe.Pointer(&element))) + SysFreeString(element) + return +} + +// safeArrayGetIID is the InterfaceID of the elements in the SafeArray. +// +// AKA: SafeArrayGetIID in Windows API. +func safeArrayGetIID(safearray *SafeArray) (guid *GUID, err error) { + err = convertHresultToError( + procSafeArrayGetIID.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&guid)))) + return +} + +// safeArrayGetLBound returns lower bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetLBound in Windows API. +func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (lowerBound int32, err error) { + err = convertHresultToError( + procSafeArrayGetLBound.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(dimension), + uintptr(unsafe.Pointer(&lowerBound)))) + return +} + +// safeArrayGetUBound returns upper bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetUBound in Windows API. +func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (upperBound int32, err error) { + err = convertHresultToError( + procSafeArrayGetUBound.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(dimension), + uintptr(unsafe.Pointer(&upperBound)))) + return +} + +// safeArrayGetVartype returns data type of SafeArray. +// +// AKA: SafeArrayGetVartype in Windows API. +func safeArrayGetVartype(safearray *SafeArray) (varType uint16, err error) { + err = convertHresultToError( + procSafeArrayGetVartype.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&varType)))) + return +} + +// safeArrayLock locks SafeArray for reading to modify SafeArray. +// +// This must be called during some calls to ensure that another process does not +// read or write to the SafeArray during editing. +// +// AKA: SafeArrayLock in Windows API. +func safeArrayLock(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayLock.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayUnlock unlocks SafeArray for reading. +// +// AKA: SafeArrayUnlock in Windows API. +func safeArrayUnlock(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayUnlock.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayPutElement stores the data element at the specified location in the +// array. +// +// AKA: SafeArrayPutElement in Windows API. +func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) (err error) { + err = convertHresultToError( + procSafeArrayPutElement.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&index)), + uintptr(unsafe.Pointer(element)))) + return +} + +// safeArrayGetRecordInfo accesses IRecordInfo info for custom types. +// +// AKA: SafeArrayGetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArrayGetRecordInfo(safearray *SafeArray) (recordInfo interface{}, err error) { + err = convertHresultToError( + procSafeArrayGetRecordInfo.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&recordInfo)))) + return +} + +// safeArraySetRecordInfo mutates IRecordInfo info for custom types. +// +// AKA: SafeArraySetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) (err error) { + err = convertHresultToError( + procSafeArraySetRecordInfo.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&recordInfo)))) + return +} diff --git a/vendor/github.com/go-ole/go-ole/safearrayconversion.go b/vendor/github.com/go-ole/go-ole/safearrayconversion.go new file mode 100644 index 00000000..da737293 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/safearrayconversion.go @@ -0,0 +1,140 @@ +// Helper for converting SafeArray to array of objects. + +package ole + +import ( + "unsafe" +) + +type SafeArrayConversion struct { + Array *SafeArray +} + +func (sac *SafeArrayConversion) ToStringArray() (strings []string) { + totalElements, _ := sac.TotalElements(0) + strings = make([]string, totalElements) + + for i := int32(0); i < totalElements; i++ { + strings[int32(i)], _ = safeArrayGetElementString(sac.Array, i) + } + + return +} + +func (sac *SafeArrayConversion) ToByteArray() (bytes []byte) { + totalElements, _ := sac.TotalElements(0) + bytes = make([]byte, totalElements) + + for i := int32(0); i < totalElements; i++ { + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&bytes[int32(i)])) + } + + return +} + +func (sac *SafeArrayConversion) ToValueArray() (values []interface{}) { + totalElements, _ := sac.TotalElements(0) + values = make([]interface{}, totalElements) + vt, _ := safeArrayGetVartype(sac.Array) + + for i := int32(0); i < totalElements; i++ { + switch VT(vt) { + case VT_BOOL: + var v bool + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I1: + var v int8 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I2: + var v int16 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I4: + var v int32 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I8: + var v int64 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI1: + var v uint8 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI2: + var v uint16 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI4: + var v uint32 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI8: + var v uint64 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_R4: + var v float32 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_R8: + var v float64 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_BSTR: + v , _ := safeArrayGetElementString(sac.Array, i) + values[i] = v + case VT_VARIANT: + var v VARIANT + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v.Value() + v.Clear() + default: + // TODO + } + } + + return +} + +func (sac *SafeArrayConversion) GetType() (varType uint16, err error) { + return safeArrayGetVartype(sac.Array) +} + +func (sac *SafeArrayConversion) GetDimensions() (dimensions *uint32, err error) { + return safeArrayGetDim(sac.Array) +} + +func (sac *SafeArrayConversion) GetSize() (length *uint32, err error) { + return safeArrayGetElementSize(sac.Array) +} + +func (sac *SafeArrayConversion) TotalElements(index uint32) (totalElements int32, err error) { + if index < 1 { + index = 1 + } + + // Get array bounds + var LowerBounds int32 + var UpperBounds int32 + + LowerBounds, err = safeArrayGetLBound(sac.Array, index) + if err != nil { + return + } + + UpperBounds, err = safeArrayGetUBound(sac.Array, index) + if err != nil { + return + } + + totalElements = UpperBounds - LowerBounds + 1 + return +} + +// Release Safe Array memory +func (sac *SafeArrayConversion) Release() { + safeArrayDestroy(sac.Array) +} diff --git a/vendor/github.com/go-ole/go-ole/safearrayslices.go b/vendor/github.com/go-ole/go-ole/safearrayslices.go new file mode 100644 index 00000000..a9fa885f --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/safearrayslices.go @@ -0,0 +1,33 @@ +// +build windows + +package ole + +import ( + "unsafe" +) + +func safeArrayFromByteSlice(slice []byte) *SafeArray { + array, _ := safeArrayCreateVector(VT_UI1, 0, uint32(len(slice))) + + if array == nil { + panic("Could not convert []byte to SAFEARRAY") + } + + for i, v := range slice { + safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(&v))) + } + return array +} + +func safeArrayFromStringSlice(slice []string) *SafeArray { + array, _ := safeArrayCreateVector(VT_BSTR, 0, uint32(len(slice))) + + if array == nil { + panic("Could not convert []string to SAFEARRAY") + } + // SysAllocStringLen(s) + for i, v := range slice { + safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(SysAllocStringLen(v)))) + } + return array +} diff --git a/vendor/github.com/go-ole/go-ole/utility.go b/vendor/github.com/go-ole/go-ole/utility.go new file mode 100644 index 00000000..99ee82dc --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/utility.go @@ -0,0 +1,101 @@ +package ole + +import ( + "unicode/utf16" + "unsafe" +) + +// ClassIDFrom retrieves class ID whether given is program ID or application string. +// +// Helper that provides check against both Class ID from Program ID and Class ID from string. It is +// faster, if you know which you are using, to use the individual functions, but this will check +// against available functions for you. +func ClassIDFrom(programID string) (classID *GUID, err error) { + classID, err = CLSIDFromProgID(programID) + if err != nil { + classID, err = CLSIDFromString(programID) + if err != nil { + return + } + } + return +} + +// BytePtrToString converts byte pointer to a Go string. +func BytePtrToString(p *byte) string { + a := (*[10000]uint8)(unsafe.Pointer(p)) + i := 0 + for a[i] != 0 { + i++ + } + return string(a[:i]) +} + +// UTF16PtrToString is alias for LpOleStrToString. +// +// Kept for compatibility reasons. +func UTF16PtrToString(p *uint16) string { + return LpOleStrToString(p) +} + +// LpOleStrToString converts COM Unicode to Go string. +func LpOleStrToString(p *uint16) string { + if p == nil { + return "" + } + + length := lpOleStrLen(p) + a := make([]uint16, length) + + ptr := unsafe.Pointer(p) + + for i := 0; i < int(length); i++ { + a[i] = *(*uint16)(ptr) + ptr = unsafe.Pointer(uintptr(ptr) + 2) + } + + return string(utf16.Decode(a)) +} + +// BstrToString converts COM binary string to Go string. +func BstrToString(p *uint16) string { + if p == nil { + return "" + } + length := SysStringLen((*int16)(unsafe.Pointer(p))) + a := make([]uint16, length) + + ptr := unsafe.Pointer(p) + + for i := 0; i < int(length); i++ { + a[i] = *(*uint16)(ptr) + ptr = unsafe.Pointer(uintptr(ptr) + 2) + } + return string(utf16.Decode(a)) +} + +// lpOleStrLen returns the length of Unicode string. +func lpOleStrLen(p *uint16) (length int64) { + if p == nil { + return 0 + } + + ptr := unsafe.Pointer(p) + + for i := 0; ; i++ { + if 0 == *(*uint16)(ptr) { + length = int64(i) + break + } + ptr = unsafe.Pointer(uintptr(ptr) + 2) + } + return +} + +// convertHresultToError converts syscall to error, if call is unsuccessful. +func convertHresultToError(hr uintptr, r2 uintptr, ignore error) (err error) { + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/vendor/github.com/go-ole/go-ole/variables.go b/vendor/github.com/go-ole/go-ole/variables.go new file mode 100644 index 00000000..a6add1b0 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variables.go @@ -0,0 +1,15 @@ +// +build windows + +package ole + +import ( + "golang.org/x/sys/windows" +) + +var ( + modcombase = windows.NewLazySystemDLL("combase.dll") + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + modole32 = windows.NewLazySystemDLL("ole32.dll") + modoleaut32 = windows.NewLazySystemDLL("oleaut32.dll") + moduser32 = windows.NewLazySystemDLL("user32.dll") +) diff --git a/vendor/github.com/go-ole/go-ole/variant.go b/vendor/github.com/go-ole/go-ole/variant.go new file mode 100644 index 00000000..967a23fe --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant.go @@ -0,0 +1,105 @@ +package ole + +import "unsafe" + +// NewVariant returns new variant based on type and value. +func NewVariant(vt VT, val int64) VARIANT { + return VARIANT{VT: vt, Val: val} +} + +// ToIUnknown converts Variant to Unknown object. +func (v *VARIANT) ToIUnknown() *IUnknown { + if v.VT != VT_UNKNOWN { + return nil + } + return (*IUnknown)(unsafe.Pointer(uintptr(v.Val))) +} + +// ToIDispatch converts variant to dispatch object. +func (v *VARIANT) ToIDispatch() *IDispatch { + if v.VT != VT_DISPATCH { + return nil + } + return (*IDispatch)(unsafe.Pointer(uintptr(v.Val))) +} + +// ToArray converts variant to SafeArray helper. +func (v *VARIANT) ToArray() *SafeArrayConversion { + if v.VT != VT_SAFEARRAY { + if v.VT&VT_ARRAY == 0 { + return nil + } + } + var safeArray *SafeArray = (*SafeArray)(unsafe.Pointer(uintptr(v.Val))) + return &SafeArrayConversion{safeArray} +} + +// ToString converts variant to Go string. +func (v *VARIANT) ToString() string { + if v.VT != VT_BSTR { + return "" + } + return BstrToString(*(**uint16)(unsafe.Pointer(&v.Val))) +} + +// Clear the memory of variant object. +func (v *VARIANT) Clear() error { + return VariantClear(v) +} + +// Value returns variant value based on its type. +// +// Currently supported types: 2- and 4-byte integers, strings, bools. +// Note that 64-bit integers, datetimes, and other types are stored as strings +// and will be returned as strings. +// +// Needs to be further converted, because this returns an interface{}. +func (v *VARIANT) Value() interface{} { + switch v.VT { + case VT_I1: + return int8(v.Val) + case VT_UI1: + return uint8(v.Val) + case VT_I2: + return int16(v.Val) + case VT_UI2: + return uint16(v.Val) + case VT_I4: + return int32(v.Val) + case VT_UI4: + return uint32(v.Val) + case VT_I8: + return int64(v.Val) + case VT_UI8: + return uint64(v.Val) + case VT_INT: + return int(v.Val) + case VT_UINT: + return uint(v.Val) + case VT_INT_PTR: + return uintptr(v.Val) // TODO + case VT_UINT_PTR: + return uintptr(v.Val) + case VT_R4: + return *(*float32)(unsafe.Pointer(&v.Val)) + case VT_R8: + return *(*float64)(unsafe.Pointer(&v.Val)) + case VT_BSTR: + return v.ToString() + case VT_DATE: + // VT_DATE type will either return float64 or time.Time. + d := uint64(v.Val) + date, err := GetVariantDate(d) + if err != nil { + return float64(v.Val) + } + return date + case VT_UNKNOWN: + return v.ToIUnknown() + case VT_DISPATCH: + return v.ToIDispatch() + case VT_BOOL: + return v.Val != 0 + } + return nil +} diff --git a/vendor/github.com/go-ole/go-ole/variant_386.go b/vendor/github.com/go-ole/go-ole/variant_386.go new file mode 100644 index 00000000..e73736bf --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_386.go @@ -0,0 +1,11 @@ +// +build 386 + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 +} diff --git a/vendor/github.com/go-ole/go-ole/variant_amd64.go b/vendor/github.com/go-ole/go-ole/variant_amd64.go new file mode 100644 index 00000000..dccdde13 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_amd64.go @@ -0,0 +1,12 @@ +// +build amd64 + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 + _ [8]byte // 24 +} diff --git a/vendor/github.com/go-ole/go-ole/variant_arm.go b/vendor/github.com/go-ole/go-ole/variant_arm.go new file mode 100644 index 00000000..d4724544 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_arm.go @@ -0,0 +1,11 @@ +// +build arm + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 +} diff --git a/vendor/github.com/go-ole/go-ole/variant_arm64.go b/vendor/github.com/go-ole/go-ole/variant_arm64.go new file mode 100644 index 00000000..78473cec --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_arm64.go @@ -0,0 +1,13 @@ +//go:build arm64 +// +build arm64 + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 + _ [8]byte // 24 +} diff --git a/vendor/github.com/go-ole/go-ole/variant_date_386.go b/vendor/github.com/go-ole/go-ole/variant_date_386.go new file mode 100644 index 00000000..1b970f63 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_date_386.go @@ -0,0 +1,22 @@ +// +build windows,386 + +package ole + +import ( + "errors" + "syscall" + "time" + "unsafe" +) + +// GetVariantDate converts COM Variant Time value to Go time.Time. +func GetVariantDate(value uint64) (time.Time, error) { + var st syscall.Systemtime + v1 := uint32(value) + v2 := uint32(value >> 32) + r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st))) + if r != 0 { + return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil + } + return time.Now(), errors.New("Could not convert to time, passing current time.") +} diff --git a/vendor/github.com/go-ole/go-ole/variant_date_amd64.go b/vendor/github.com/go-ole/go-ole/variant_date_amd64.go new file mode 100644 index 00000000..6952f1f0 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_date_amd64.go @@ -0,0 +1,20 @@ +// +build windows,amd64 + +package ole + +import ( + "errors" + "syscall" + "time" + "unsafe" +) + +// GetVariantDate converts COM Variant Time value to Go time.Time. +func GetVariantDate(value uint64) (time.Time, error) { + var st syscall.Systemtime + r, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st))) + if r != 0 { + return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil + } + return time.Now(), errors.New("Could not convert to time, passing current time.") +} diff --git a/vendor/github.com/go-ole/go-ole/variant_date_arm.go b/vendor/github.com/go-ole/go-ole/variant_date_arm.go new file mode 100644 index 00000000..09ec7b5c --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_date_arm.go @@ -0,0 +1,22 @@ +// +build windows,arm + +package ole + +import ( + "errors" + "syscall" + "time" + "unsafe" +) + +// GetVariantDate converts COM Variant Time value to Go time.Time. +func GetVariantDate(value uint64) (time.Time, error) { + var st syscall.Systemtime + v1 := uint32(value) + v2 := uint32(value >> 32) + r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st))) + if r != 0 { + return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil + } + return time.Now(), errors.New("Could not convert to time, passing current time.") +} diff --git a/vendor/github.com/go-ole/go-ole/variant_date_arm64.go b/vendor/github.com/go-ole/go-ole/variant_date_arm64.go new file mode 100644 index 00000000..02b04a0d --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_date_arm64.go @@ -0,0 +1,23 @@ +//go:build windows && arm64 +// +build windows,arm64 + +package ole + +import ( + "errors" + "syscall" + "time" + "unsafe" +) + +// GetVariantDate converts COM Variant Time value to Go time.Time. +func GetVariantDate(value uint64) (time.Time, error) { + var st syscall.Systemtime + v1 := uint32(value) + v2 := uint32(value >> 32) + r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st))) + if r != 0 { + return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil + } + return time.Now(), errors.New("Could not convert to time, passing current time.") +} diff --git a/vendor/github.com/go-ole/go-ole/variant_ppc64le.go b/vendor/github.com/go-ole/go-ole/variant_ppc64le.go new file mode 100644 index 00000000..326427a7 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_ppc64le.go @@ -0,0 +1,12 @@ +// +build ppc64le + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 + _ [8]byte // 24 +} diff --git a/vendor/github.com/go-ole/go-ole/variant_s390x.go b/vendor/github.com/go-ole/go-ole/variant_s390x.go new file mode 100644 index 00000000..9874ca66 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/variant_s390x.go @@ -0,0 +1,12 @@ +// +build s390x + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 + _ [8]byte // 24 +} diff --git a/vendor/github.com/go-ole/go-ole/vt_string.go b/vendor/github.com/go-ole/go-ole/vt_string.go new file mode 100644 index 00000000..729b4a04 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/vt_string.go @@ -0,0 +1,58 @@ +// generated by stringer -output vt_string.go -type VT; DO NOT EDIT + +package ole + +import "fmt" + +const ( + _VT_name_0 = "VT_EMPTYVT_NULLVT_I2VT_I4VT_R4VT_R8VT_CYVT_DATEVT_BSTRVT_DISPATCHVT_ERRORVT_BOOLVT_VARIANTVT_UNKNOWNVT_DECIMAL" + _VT_name_1 = "VT_I1VT_UI1VT_UI2VT_UI4VT_I8VT_UI8VT_INTVT_UINTVT_VOIDVT_HRESULTVT_PTRVT_SAFEARRAYVT_CARRAYVT_USERDEFINEDVT_LPSTRVT_LPWSTR" + _VT_name_2 = "VT_RECORDVT_INT_PTRVT_UINT_PTR" + _VT_name_3 = "VT_FILETIMEVT_BLOBVT_STREAMVT_STORAGEVT_STREAMED_OBJECTVT_STORED_OBJECTVT_BLOB_OBJECTVT_CFVT_CLSID" + _VT_name_4 = "VT_BSTR_BLOBVT_VECTOR" + _VT_name_5 = "VT_ARRAY" + _VT_name_6 = "VT_BYREF" + _VT_name_7 = "VT_RESERVED" + _VT_name_8 = "VT_ILLEGAL" +) + +var ( + _VT_index_0 = [...]uint8{0, 8, 15, 20, 25, 30, 35, 40, 47, 54, 65, 73, 80, 90, 100, 110} + _VT_index_1 = [...]uint8{0, 5, 11, 17, 23, 28, 34, 40, 47, 54, 64, 70, 82, 91, 105, 113, 122} + _VT_index_2 = [...]uint8{0, 9, 19, 30} + _VT_index_3 = [...]uint8{0, 11, 18, 27, 37, 55, 71, 85, 90, 98} + _VT_index_4 = [...]uint8{0, 12, 21} + _VT_index_5 = [...]uint8{0, 8} + _VT_index_6 = [...]uint8{0, 8} + _VT_index_7 = [...]uint8{0, 11} + _VT_index_8 = [...]uint8{0, 10} +) + +func (i VT) String() string { + switch { + case 0 <= i && i <= 14: + return _VT_name_0[_VT_index_0[i]:_VT_index_0[i+1]] + case 16 <= i && i <= 31: + i -= 16 + return _VT_name_1[_VT_index_1[i]:_VT_index_1[i+1]] + case 36 <= i && i <= 38: + i -= 36 + return _VT_name_2[_VT_index_2[i]:_VT_index_2[i+1]] + case 64 <= i && i <= 72: + i -= 64 + return _VT_name_3[_VT_index_3[i]:_VT_index_3[i+1]] + case 4095 <= i && i <= 4096: + i -= 4095 + return _VT_name_4[_VT_index_4[i]:_VT_index_4[i+1]] + case i == 8192: + return _VT_name_5 + case i == 16384: + return _VT_name_6 + case i == 32768: + return _VT_name_7 + case i == 65535: + return _VT_name_8 + default: + return fmt.Sprintf("VT(%d)", i) + } +} diff --git a/vendor/github.com/go-ole/go-ole/winrt.go b/vendor/github.com/go-ole/go-ole/winrt.go new file mode 100644 index 00000000..4e9eca73 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/winrt.go @@ -0,0 +1,99 @@ +// +build windows + +package ole + +import ( + "reflect" + "syscall" + "unicode/utf8" + "unsafe" +) + +var ( + procRoInitialize = modcombase.NewProc("RoInitialize") + procRoActivateInstance = modcombase.NewProc("RoActivateInstance") + procRoGetActivationFactory = modcombase.NewProc("RoGetActivationFactory") + procWindowsCreateString = modcombase.NewProc("WindowsCreateString") + procWindowsDeleteString = modcombase.NewProc("WindowsDeleteString") + procWindowsGetStringRawBuffer = modcombase.NewProc("WindowsGetStringRawBuffer") +) + +func RoInitialize(thread_type uint32) (err error) { + hr, _, _ := procRoInitialize.Call(uintptr(thread_type)) + if hr != 0 { + err = NewError(hr) + } + return +} + +func RoActivateInstance(clsid string) (ins *IInspectable, err error) { + hClsid, err := NewHString(clsid) + if err != nil { + return nil, err + } + defer DeleteHString(hClsid) + + hr, _, _ := procRoActivateInstance.Call( + uintptr(unsafe.Pointer(hClsid)), + uintptr(unsafe.Pointer(&ins))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func RoGetActivationFactory(clsid string, iid *GUID) (ins *IInspectable, err error) { + hClsid, err := NewHString(clsid) + if err != nil { + return nil, err + } + defer DeleteHString(hClsid) + + hr, _, _ := procRoGetActivationFactory.Call( + uintptr(unsafe.Pointer(hClsid)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&ins))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// HString is handle string for pointers. +type HString uintptr + +// NewHString returns a new HString for Go string. +func NewHString(s string) (hstring HString, err error) { + u16 := syscall.StringToUTF16Ptr(s) + len := uint32(utf8.RuneCountInString(s)) + hr, _, _ := procWindowsCreateString.Call( + uintptr(unsafe.Pointer(u16)), + uintptr(len), + uintptr(unsafe.Pointer(&hstring))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// DeleteHString deletes HString. +func DeleteHString(hstring HString) (err error) { + hr, _, _ := procWindowsDeleteString.Call(uintptr(hstring)) + if hr != 0 { + err = NewError(hr) + } + return +} + +// String returns Go string value of HString. +func (h HString) String() string { + var u16buf uintptr + var u16len uint32 + u16buf, _, _ = procWindowsGetStringRawBuffer.Call( + uintptr(h), + uintptr(unsafe.Pointer(&u16len))) + + u16hdr := reflect.SliceHeader{Data: u16buf, Len: int(u16len), Cap: int(u16len)} + u16 := *(*[]uint16)(unsafe.Pointer(&u16hdr)) + return syscall.UTF16ToString(u16) +} diff --git a/vendor/github.com/go-ole/go-ole/winrt_doc.go b/vendor/github.com/go-ole/go-ole/winrt_doc.go new file mode 100644 index 00000000..52e6d74c --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/winrt_doc.go @@ -0,0 +1,36 @@ +// +build !windows + +package ole + +// RoInitialize +func RoInitialize(thread_type uint32) (err error) { + return NewError(E_NOTIMPL) +} + +// RoActivateInstance +func RoActivateInstance(clsid string) (ins *IInspectable, err error) { + return nil, NewError(E_NOTIMPL) +} + +// RoGetActivationFactory +func RoGetActivationFactory(clsid string, iid *GUID) (ins *IInspectable, err error) { + return nil, NewError(E_NOTIMPL) +} + +// HString is handle string for pointers. +type HString uintptr + +// NewHString returns a new HString for Go string. +func NewHString(s string) (hstring HString, err error) { + return HString(uintptr(0)), NewError(E_NOTIMPL) +} + +// DeleteHString deletes HString. +func DeleteHString(hstring HString) (err error) { + return NewError(E_NOTIMPL) +} + +// String returns Go string value of HString. +func (h HString) String() string { + return "" +} diff --git a/vendor/github.com/longhorn/go-common-libs/exec/exec.go b/vendor/github.com/longhorn/go-common-libs/exec/exec.go new file mode 100644 index 00000000..8efd15d4 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/exec/exec.go @@ -0,0 +1,125 @@ +package exec + +import ( + "bytes" + "context" + "io" + "os" + "os/exec" + "strings" + "time" + + "github.com/pkg/errors" + + "github.com/longhorn/go-common-libs/types" +) + +// ExecuteInterface is the interface for executing commands. +type ExecuteInterface interface { + Execute(envs []string, binary string, args []string, timeout time.Duration) (string, error) + ExecuteWithStdin(binary string, args []string, stdinString string, timeout time.Duration) (string, error) + ExecuteWithStdinPipe(binary string, args []string, stdinString string, timeout time.Duration) (string, error) +} + +// NewExecutor returns a new Executor. +func NewExecutor() ExecuteInterface { + return &Executor{} +} + +// Executor is the implementation of ExecuteInterface. +type Executor struct{} + +// Execute executes the given command with the specified environment variables, binary, and arguments. +// It returns the command's output and any occurred error. +func (e *Executor) Execute(envs []string, binary string, args []string, timeout time.Duration) (string, error) { + // If the timeout is set to -1, execute the command without any timeout. + // Otherwise, execute the command with the specified timeout. + switch timeout { + case types.ExecuteNoTimeout: + return e.executeWithoutTimeout(envs, binary, args) + default: + return e.executeWithTimeout(envs, binary, args, timeout) + } +} + +// ExecuteWithTimeout executes the command with timeout. +func (e *Executor) executeWithTimeout(envs []string, binary string, args []string, timeout time.Duration) (string, error) { + cmd := exec.Command(binary, args...) + cmd.Env = append(os.Environ(), envs...) + return e.executeCmd(cmd, timeout) +} + +// ExecuteWithoutTimeout executes the command without timeout. +func (e *Executor) executeWithoutTimeout(envs []string, binary string, args []string) (string, error) { + cmd := exec.Command(binary, args...) + cmd.Env = append(os.Environ(), envs...) + + var output, stderr bytes.Buffer + cmd.Stdout = &output + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return output.String(), errors.Wrapf(err, "failed to execute: %v %v, output %s, stderr %s", + binary, args, output.String(), stderr.String()) + } + return output.String(), nil +} + +// executeCmd executes the command with timeout. If timeout is 0, it will use default timeout. +func (e *Executor) executeCmd(cmd *exec.Cmd, timeout time.Duration) (string, error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + var output, stderr bytes.Buffer + cmd.Stdout = &output + cmd.Stderr = &stderr + + errChan := make(chan error, 1) + go func() { + errChan <- cmd.Run() + }() + + select { + case err := <-errChan: + if err != nil { + return "", errors.Wrapf(err, "failed to execute: %v %v, output %s, stderr %s", + cmd.Path, cmd.Args, output.String(), stderr.String()) + } + case <-ctx.Done(): + return "", errors.Errorf("timeout executing: %v %v", cmd.Path, cmd.Args) + } + + return output.String(), nil +} + +// ExecuteWithStdin executes the command with stdin. +func (e *Executor) ExecuteWithStdin(binary string, args []string, stdinString string, timeout time.Duration) (string, error) { + cmd := exec.Command(binary, args...) + cmd.Env = os.Environ() + + if stdinString != "" { + cmd.Stdin = strings.NewReader(stdinString) + } + + return e.executeCmd(cmd, timeout) +} + +// ExecuteWithStdinPipe executes the command with stdin pipe. +func (e *Executor) ExecuteWithStdinPipe(binary string, args []string, stdinString string, timeout time.Duration) (string, error) { + cmd := exec.Command(binary, args...) + cmd.Env = os.Environ() + + stdin, err := cmd.StdinPipe() + if err != nil { + return "", err + } + + go func() { + defer func() { + _ = stdin.Close() + }() + _, _ = io.WriteString(stdin, stdinString) + }() + + return e.executeCmd(cmd, timeout) +} diff --git a/vendor/github.com/longhorn/go-common-libs/io/file.go b/vendor/github.com/longhorn/go-common-libs/io/file.go new file mode 100644 index 00000000..9bd9d4cc --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/io/file.go @@ -0,0 +1,361 @@ +package io + +import ( + "fmt" + "io" + "os" + "path/filepath" + "strings" + "syscall" + "time" + + "github.com/pkg/errors" + "github.com/shirou/gopsutil/v3/disk" + "github.com/sirupsen/logrus" + + "github.com/longhorn/go-common-libs/types" +) + +// CreateDirectory creates a directory at the specified path, and sets the modification time. +func CreateDirectory(path string, modTime time.Time) (string, error) { + // Check if the directory already exists + if fileInfo, err := os.Stat(path); err == nil && fileInfo.IsDir() { + return filepath.Abs(path) + } + + // Create the directory and its parent directories if they don't exist. + err := os.MkdirAll(path, 0755) + if err != nil { + return "", err + } + + // Set the modification time of the directory. + err = os.Chtimes(path, modTime, modTime) + if err != nil { + return "", errors.Wrapf(err, "failed to set the modification time for %v", path) + } + + // Return the absolute path of the directory. + return filepath.Abs(path) +} + +// CopyDirectory copies the directory from source to destination. +func CopyDirectory(sourcePath, destinationPath string, doOverWrite bool) error { + var err error + defer func() { + err = errors.Wrapf(err, "failed to copy directory %v to %v", sourcePath, destinationPath) + }() + + sourcePathInfo, err := os.Stat(sourcePath) + if err != nil { + return err + } + + destinationAbsPath, err := CreateDirectory(destinationPath, sourcePathInfo.ModTime()) + if err != nil { + return err + } + + return CopyFiles(sourcePath, destinationAbsPath, doOverWrite) +} + +// CopyFiles copies the files from source to destination. +func CopyFiles(sourcePath, destinationPath string, doOverWrite bool) error { + srcFileInfo, err := os.Stat(sourcePath) + if err != nil { + return err + } + + if !srcFileInfo.IsDir() { + return CopyFile(sourcePath, destinationPath, doOverWrite) + } + + srcFileInfos, err := os.ReadDir(sourcePath) + if err != nil { + return errors.Wrapf(err, "failed to read source directory %v", sourcePath) + } + + for _, srcFileInfo := range srcFileInfos { + srcFileInfo, err := srcFileInfo.Info() + if err != nil { + return err + } + + dstFilePath := filepath.Join(destinationPath, srcFileInfo.Name()) + srcFilePath := filepath.Join(sourcePath, srcFileInfo.Name()) + + if srcFileInfo.IsDir() { + if err := CopyDirectory(srcFilePath, dstFilePath, doOverWrite); err != nil { + return err + } + } else { + if err := CopyFile(srcFilePath, dstFilePath, doOverWrite); err != nil { + return err + } + } + } + + return nil +} + +// CopyFile copies the file from source to destination. +func CopyFile(sourcePath, destinationPath string, overWrite bool) error { + var err error + defer func() { + err = errors.Wrapf(err, "failed to copy file %v to %v", sourcePath, destinationPath) + }() + + sourceFileInfo, err := os.Stat(sourcePath) + if err != nil { + return err + } + + if !overWrite { + if _, err := os.Stat(destinationPath); err == nil { + logrus.Warnf("destination file %v already exists", destinationPath) + return nil + } + } + + sourceFile, err := os.Open(sourcePath) + if err != nil { + return err + } + defer sourceFile.Close() + + _, err = CreateDirectory(filepath.Dir(destinationPath), sourceFileInfo.ModTime()) + if err != nil { + return err + } + + destinationFile, err := os.Create(destinationPath) + if err != nil { + return err + } + defer destinationFile.Close() + + _, err = io.Copy(destinationFile, sourceFile) + if err != nil { + return err + } + + // Set the modification time of the file. + sourceFileModTime := sourceFileInfo.ModTime() + return os.Chtimes(destinationPath, sourceFileModTime, sourceFileModTime) +} + +// GetEmptyFiles retrieves a list of paths for all empty files within the specified directory. +// It uses filepath.Walk to traverse the directory and finds files with zero size. +// It returns a slice of filePaths and an error if any. +func GetEmptyFiles(directory string) (filePaths []string, err error) { + err = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error { + if err != nil { + return errors.Wrapf(err, "failed to walk directory %v", directory) + } + if info.IsDir() { + return nil + } + if info.Size() == 0 { + filePaths = append(filePaths, path) + } + return nil + }) + return filePaths, errors.Wrapf(err, "failed to get empty files in %s", directory) +} + +// FindFiles searches for files in the specified directory with the given fileName. +// If fileName is empty, it retrieves all files in the directory. +// If maxDepth is greater than 0, it limits the search to the specified depth. +// It returns a slice of filePaths and an error if any. +func FindFiles(directory, fileName string, maxDepth int) (filePaths []string, err error) { + err = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error { + if err != nil { + // If the directory contains symbolic links, it might lead to a non-existing file error. + // Ignore this error and continue walking the directory. + logrus.WithError(err).Warn("Encountered error while searching for files") + return nil + } + + // Calculate the depth of the directory + depth := strings.Count(strings.TrimPrefix(path, directory), string(os.PathSeparator)) + + // Skip the directory if it exceeds the maximum depth + if maxDepth > 0 && depth > maxDepth { + if info.IsDir() { + return filepath.SkipDir // Skip this directory + } + + return nil // Skip this file + } + + if fileName == "" || info.Name() == fileName { + filePaths = append(filePaths, path) + } + return nil + }) + return filePaths, errors.Wrapf(err, "failed to find files in %s", directory) +} + +// ReadFileContent reads the content of the file. +func ReadFileContent(filePath string) (string, error) { + if _, err := os.Stat(filePath); err != nil { + return "", errors.Wrapf(err, "cannot find file %v", filePath) + } + + content, err := os.ReadFile(filePath) + if err != nil { + return "", err + } + + return string(content), nil +} + +// SyncFile syncs the file to the disk. +func SyncFile(filePath string) error { + file, err := os.OpenFile(filePath, os.O_RDWR, 0644) + if err != nil { + return err + } + defer file.Close() + + return file.Sync() +} + +// GetDiskStat returns the disk stat for the specified path. +func GetDiskStat(path string) (diskStat types.DiskStat, err error) { + defer func() { + err = errors.Wrapf(err, "failed to get fs stat for %v", path) + }() + + var statfs syscall.Statfs_t + if err := syscall.Statfs(path, &statfs); err != nil { + return diskStat, err + } + + usage, err := disk.Usage(path) + if err != nil { + return diskStat, err + } + + // Convert the FSID components to a single uint64 FSID value + var fsidValue uint64 + for _, component := range statfs.Fsid.X__val { + // Combine components using bit manipulation + fsidValue = (fsidValue << 32) | uint64(uint32(component)) + } + + // Format the FSID value with leading zeros + fsidFormatted := fmt.Sprintf("%012x", fsidValue) + + return types.DiskStat{ + DiskID: fsidFormatted, + Path: path, + Type: usage.Fstype, + Driver: types.DiskDriverNone, + FreeBlocks: int64(statfs.Bfree), + TotalBlocks: int64(statfs.Blocks), + BlockSize: statfs.Bsize, + StorageMaximum: int64(statfs.Blocks) * statfs.Bsize, + StorageAvailable: int64(statfs.Bfree) * statfs.Bsize, + }, nil +} + +// ListOpenFiles returns a list of open files in the specified directory. +func ListOpenFiles(procDirectory, directory string) ([]string, error) { + // Check if the specified directory exists + if _, err := os.Stat(directory); err != nil { + return nil, err + } + + // Get the list of all processes in the provided procDirectory + procs, err := os.ReadDir(procDirectory) + if err != nil { + return nil, err + } + + // Iterate over each process in the procDirectory + var openedFiles []string + for _, proc := range procs { + // Skip non-directory entries + if !proc.IsDir() { + continue + } + + // Read the file descriptor directory for the process + pid := proc.Name() + fdDir := filepath.Join(procDirectory, pid, "fd") + files, err := os.ReadDir(fdDir) + if err != nil { + logrus.WithError(err).Tracef("Failed to read file descriptors for process %v", pid) + continue + } + + // Iterate over each file in the file descriptor directory + for _, file := range files { + filePath, err := os.Readlink(filepath.Join(fdDir, file.Name())) + if err != nil { + logrus.WithError(err).Tracef("Failed to read link for file descriptor %v", file.Name()) + continue + } + + // Check if the file path is within the specified directory + if strings.HasPrefix(filePath, directory+"/") || filePath == directory { + openedFiles = append(openedFiles, filePath) + } + } + } + + return openedFiles, nil +} + +// IsDirectoryEmpty returns true if the specified directory is empty. +func IsDirectoryEmpty(directory string) (bool, error) { + f, err := os.Open(directory) + if err != nil { + return false, err + } + defer f.Close() + + _, err = f.Readdirnames(1) + if err == io.EOF { + return true, nil + } + if err != nil { + return false, err + } + + return false, nil +} + +// CheckIsFileSizeSame verifies if all files in the provided paths have the same size. +// It returns an error if any file is a directory, does not exist, or has a different size. +func CheckIsFileSizeSame(paths ...string) error { + referenceInfo, err := os.Stat(paths[0]) + if err != nil { + return err + } + + if referenceInfo.IsDir() { + return errors.Errorf("file %v is a directory", paths[0]) + } + + referenceSize := referenceInfo.Size() + + for _, path := range paths { + fileInfo, err := os.Stat(path) + if err != nil { + return err + } + + if fileInfo.IsDir() { + return errors.Errorf("file %v is a directory", path) + + } + + if fileInfo.Size() != referenceSize { + return errors.Errorf("file %v size %v is not equal to %v", path, fileInfo.Size(), referenceSize) + } + } + + return nil +} diff --git a/vendor/github.com/longhorn/go-common-libs/ns/crypto.go b/vendor/github.com/longhorn/go-common-libs/ns/crypto.go new file mode 100644 index 00000000..5638b37d --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/ns/crypto.go @@ -0,0 +1,69 @@ +package ns + +import ( + "time" + + "github.com/longhorn/go-common-libs/types" +) + +// LuksOpen runs cryptsetup luksOpen with the given passphrase and +// returns the stdout and error. +func (nsexec *Executor) LuksOpen(volume, devicePath, passphrase string, timeout time.Duration) (stdout string, err error) { + args := []string{"luksOpen", devicePath, volume, "-d", "/dev/stdin"} + return nsexec.CryptsetupWithPassphrase(passphrase, args, timeout) +} + +// LuksClose runs cryptsetup luksClose and returns the stdout and error. +func (nsexec *Executor) LuksClose(volume string, timeout time.Duration) (stdout string, err error) { + args := []string{"luksClose", volume} + return nsexec.Cryptsetup(args, timeout) +} + +// LuksFormat runs cryptsetup luksFormat with the given passphrase and +// returns the stdout and error. +func (nsexec *Executor) LuksFormat(devicePath, passphrase, keyCipher, keyHash, keySize, pbkdf string, timeout time.Duration) (stdout string, err error) { + args := []string{ + "-q", "luksFormat", + "--type", "luks2", + "--cipher", keyCipher, + "--hash", keyHash, + "--key-size", keySize, + "--pbkdf", pbkdf, + devicePath, "-d", "/dev/stdin", + } + return nsexec.CryptsetupWithPassphrase(passphrase, args, timeout) +} + +// LuksResize runs cryptsetup resize with the given passphrase and +// returns the stdout and error. +func (nsexec *Executor) LuksResize(volume, passphrase string, timeout time.Duration) (stdout string, err error) { + args := []string{"resize", volume} + return nsexec.CryptsetupWithPassphrase(passphrase, args, timeout) +} + +// LuksStatus runs cryptsetup status and returns the stdout and error. +func (nsexec *Executor) LuksStatus(volume string, timeout time.Duration) (stdout string, err error) { + args := []string{"status", volume} + return nsexec.Cryptsetup(args, timeout) +} + +// Cryptsetup runs cryptsetup without passphrase. It will return +// 0 on success and a non-zero value on error. +func (nsexec *Executor) Cryptsetup(args []string, timeout time.Duration) (stdout string, err error) { + return nsexec.CryptsetupWithPassphrase("", args, timeout) +} + +// CryptsetupWithPassphrase runs cryptsetup with passphrase. It will return +// 0 on success and a non-zero value on error. +// 1 wrong parameters, 2 no permission (bad passphrase), +// 3 out of memory, 4 wrong device specified, +// 5 device already exists or device is busy. +func (nsexec *Executor) CryptsetupWithPassphrase(passphrase string, args []string, timeout time.Duration) (stdout string, err error) { + // NOTE: When using cryptsetup, ensure it is run in the host IPC/MNT namespace. + // If only the MNT namespace is used, the binary will not return, but the + // appropriate action will still be performed. + // For Talos Linux, cryptsetup comes pre-installed in the host namespace + // (ref: https://github.com/siderolabs/pkgs/blob/release-1.4/reproducibility/pkg.yaml#L10) + // for the [Disk Encryption](https://www.talos.dev/v1.4/talos-guides/configuration/disk-encryption/). + return nsexec.ExecuteWithStdin(nil, types.BinaryCryptsetup, args, passphrase, timeout) +} diff --git a/vendor/github.com/longhorn/go-common-libs/ns/executor.go b/vendor/github.com/longhorn/go-common-libs/ns/executor.go new file mode 100644 index 00000000..a850e771 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/ns/executor.go @@ -0,0 +1,87 @@ +package ns + +import ( + "path/filepath" + "time" + + "github.com/pkg/errors" + + "github.com/longhorn/go-common-libs/exec" + "github.com/longhorn/go-common-libs/proc" + "github.com/longhorn/go-common-libs/types" +) + +// Executor is a struct resonpsible for executing commands in a specific +// namespace using nsenter. +type Executor struct { + namespaces []types.Namespace // The namespaces to enter. + nsDirectory string // The directory of the namespace. + + executor exec.ExecuteInterface // An interface for executing commands. This allows mocking for unit tests. +} + +// NewNamespaceExecutor creates a new namespace executor for the given process name, +// namespaces and proc directory. If the process name is not empty, it will try to +// use the process namespace directory. Otherwise, it will use the host namespace +// directory. The namespaces are the namespaces to enter. The proc directory is +// the directory where the process information is stored. It will also verify the +// existence of the nsenter binary. +func NewNamespaceExecutor(processName, procDirectory string, namespaces []types.Namespace) (*Executor, error) { + nsDir, err := proc.GetProcessNamespaceDirectory(processName, procDirectory) + if err != nil { + return nil, err + } + + NamespaceExecutor := &Executor{ + namespaces: namespaces, + nsDirectory: nsDir, + executor: exec.NewExecutor(), + } + + if _, err := NamespaceExecutor.executor.Execute(nil, types.NsBinary, []string{"-V"}, types.ExecuteDefaultTimeout); err != nil { + return nil, errors.Wrap(err, "cannot find nsenter for namespace switching") + } + + return NamespaceExecutor, nil +} + +// prepareCommandArgs prepares the nsenter command arguments. +func (nsexec *Executor) prepareCommandArgs(binary string, args, envs []string) []string { + cmdArgs := []string{} + for _, ns := range nsexec.namespaces { + nsPath := filepath.Join(nsexec.nsDirectory, ns.String()) + switch ns { + case types.NamespaceIpc: + cmdArgs = append(cmdArgs, "--ipc="+nsPath) + case types.NamespaceMnt: + cmdArgs = append(cmdArgs, "--mount="+nsPath) + case types.NamespaceNet: + cmdArgs = append(cmdArgs, "--net="+nsPath) + } + } + if len(envs) > 0 { + cmdArgs = append(cmdArgs, "env", "-i") + cmdArgs = append(cmdArgs, envs...) + } + + cmdArgs = append(cmdArgs, binary) + return append(cmdArgs, args...) +} + +// Execute executes the command in the namespace. If NsDirectory is empty, +// it will execute the command in the current namespace. +func (nsexec *Executor) Execute(envs []string, binary string, args []string, timeout time.Duration) (string, error) { + return nsexec.executor.Execute(nil, types.NsBinary, nsexec.prepareCommandArgs(binary, args, envs), timeout) +} + +// ExecuteWithStdin executes the command in the namespace with stdin. +// If NsDirectory is empty, it will execute the command in the current namespace. +func (nsexec *Executor) ExecuteWithStdin(envs []string, binary string, args []string, stdinString string, timeout time.Duration) (string, error) { + return nsexec.executor.ExecuteWithStdin(types.NsBinary, nsexec.prepareCommandArgs(binary, args, envs), stdinString, timeout) +} + +// ExecuteWithStdinPipe executes the command in the namespace with stdin pipe. +// If NsDirectory is empty, it will execute the command in the current namespace. +func (nsexec *Executor) ExecuteWithStdinPipe(envs []string, binary string, args []string, stdinString string, timeout time.Duration) (string, error) { + return nsexec.executor.ExecuteWithStdinPipe(types.NsBinary, nsexec.prepareCommandArgs(binary, args, envs), stdinString, timeout) +} diff --git a/vendor/github.com/longhorn/go-common-libs/ns/file.go b/vendor/github.com/longhorn/go-common-libs/ns/file.go new file mode 100644 index 00000000..26fe10c5 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/ns/file.go @@ -0,0 +1,282 @@ +package ns + +import ( + "io/fs" + "os" + "path/filepath" + "strings" + "time" + + "github.com/pkg/errors" + + "github.com/longhorn/go-common-libs/io" + "github.com/longhorn/go-common-libs/types" +) + +// CopyDirectory switches to the host namespace and copies the content from +// source to destination. It will overwrite the destination if overWrite is true. +// Top level directory is prohibited. +func CopyDirectory(source, destination string, overWrite bool) (err error) { + defer func() { + err = errors.Wrapf(err, "failed to copy host content from %v to %v", source, destination) + }() + + srcDir, err := filepath.Abs(filepath.Clean(source)) + if err != nil { + return err + } + + dstDir, err := filepath.Abs(filepath.Clean(destination)) + if err != nil { + return err + } + + if strings.Count(srcDir, "/") < 2 || strings.Count(dstDir, "/") < 2 { + return errors.Errorf("prohibit copying the content for the top level of directory %v or %v", srcDir, dstDir) + } + + fn := func() (interface{}, error) { + return "", io.CopyFiles(source, destination, overWrite) + } + + _, err = RunFunc(fn, 0) + return err +} + +// CreateDirectory switches to the host namespace and creates a directory at +// the specified path. +func CreateDirectory(path string, modTime time.Time) (result string, err error) { + defer func() { + err = errors.Wrapf(err, "failed to create directory %s", path) + }() + + fn := func() (interface{}, error) { + return io.CreateDirectory(path, modTime) + } + + rawResult, err := RunFunc(fn, 0) + if err != nil { + return "", err + } + + var ableToCast bool + result, ableToCast = rawResult.(string) + if !ableToCast { + return "", errors.Errorf(types.ErrNamespaceCastResultFmt, result, rawResult) + } + return result, nil +} + +// DeleteDirectory switches to the host namespace and removes the directory +// at the specified path. +func DeleteDirectory(directory string) (err error) { + defer func() { + err = errors.Wrapf(err, "failed to remove host directory %v", directory) + }() + + dir, err := filepath.Abs(filepath.Clean(directory)) + if err != nil { + return err + } + + if strings.Count(dir, "/") < 2 { + return errors.Errorf("prohibit removing the top level of directory %v", dir) + } + + fn := func() (interface{}, error) { + if _, err := os.Stat(dir); err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + + return nil, os.RemoveAll(dir) + } + + _, err = RunFunc(fn, 0) + return err +} + +// ReadDirectory switches to the host namespace and reads the content of the +// directory at the specified path. +func ReadDirectory(directory string) (result []fs.DirEntry, err error) { + defer func() { + err = errors.Wrapf(err, "failed to read directory %s", directory) + }() + + fn := func() (interface{}, error) { + return os.ReadDir(directory) + } + + rawResult, err := RunFunc(fn, 0) + if err != nil { + return nil, err + } + + var ableToCast bool + result, ableToCast = rawResult.([]fs.DirEntry) + if !ableToCast { + return nil, errors.Errorf(types.ErrNamespaceCastResultFmt, result, rawResult) + } + return result, nil +} + +// CopyFiles switches to the host namespace and copies the all files from +// source to destination. It will overwrite the destination if overWrite is true. +func CopyFiles(sourcePath, destinationPath string, doOverWrite bool) (err error) { + defer func() { + err = errors.Wrapf(err, "failed to copy files from %s to %s", sourcePath, destinationPath) + }() + + fn := func() (interface{}, error) { + return "", io.CopyFiles(sourcePath, destinationPath, doOverWrite) + } + + _, err = RunFunc(fn, 0) + return err +} + +// GetEmptyFiles switches to the host namespace and retrieves a list +// of paths for all empty files within the specified directory. +func GetEmptyFiles(directory string) (result []string, err error) { + defer func() { + err = errors.Wrapf(err, "failed to get empty files in %s", directory) + }() + + fn := func() (interface{}, error) { + return io.GetEmptyFiles(directory) + } + + rawResult, err := RunFunc(fn, 0) + if err != nil { + return nil, err + } + + var ableToCast bool + result, ableToCast = rawResult.([]string) + if !ableToCast { + return nil, errors.Errorf(types.ErrNamespaceCastResultFmt, result, rawResult) + } + return result, nil +} + +// GetFileInfo switches to the host namespace and returns the file info of +// the file at the specified path. +func GetFileInfo(path string) (result fs.FileInfo, err error) { + defer func() { + err = errors.Wrapf(err, "failed to get file info of %s", path) + }() + + fn := func() (interface{}, error) { + return os.Stat(path) + } + + rawResult, err := RunFunc(fn, 0) + if err != nil { + return nil, err + } + + var ableToCast bool + result, ableToCast = rawResult.(fs.FileInfo) + if !ableToCast { + return nil, errors.Errorf(types.ErrNamespaceCastResultFmt, result, rawResult) + } + return result, nil +} + +// ReadFileContent switches to the host namespace and returns the content of +// the file at the specified path. +func ReadFileContent(filePath string) (result string, err error) { + defer func() { + err = errors.Wrapf(err, "failed to read file content of %s", filePath) + }() + + fn := func() (interface{}, error) { + return io.ReadFileContent(filePath) + } + + rawResult, err := RunFunc(fn, 0) + if err != nil { + return "", err + } + + var ableToCast bool + result, ableToCast = rawResult.(string) + if !ableToCast { + return "", errors.Errorf(types.ErrNamespaceCastResultFmt, result, rawResult) + } + return result, nil +} + +// SyncFile switches to the host namespace and syncs the file at the +// specified path. +func SyncFile(filePath string) (err error) { + defer func() { + err = errors.Wrapf(err, "failed to sync file %s", filePath) + }() + + fn := func() (interface{}, error) { + return nil, io.SyncFile(filePath) + } + + _, err = RunFunc(fn, 0) + return err +} + +// WriteFile switches to the host namespace and writes the data to the file +// at the specified path. +func WriteFile(filePath, data string) error { + var err error + defer func() { + err = errors.Wrapf(err, "failed to write file %s", filePath) + }() + + fn := func() (interface{}, error) { + return "", os.WriteFile(filePath, []byte(data), 0644) + } + + _, err = RunFunc(fn, 0) + return err +} + +// DeletePath switches to the host namespace and removes the file or +// directory at the specified path. +func DeletePath(path string) error { + var err error + defer func() { + err = errors.Wrapf(err, "failed to delete path %s", path) + }() + + fn := func() (interface{}, error) { + return "", os.RemoveAll(path) + } + + _, err = RunFunc(fn, 0) + return err +} + +// GetDiskStat switches to the host namespace and returns the disk stat +// of the disk at the specified path. +func GetDiskStat(path string) (*types.DiskStat, error) { + var err error + defer func() { + err = errors.Wrapf(err, "failed to get disk stat %s", path) + }() + + fn := func() (interface{}, error) { + return io.GetDiskStat(path) + } + + rawResult, err := RunFunc(fn, 0) + if err != nil { + return nil, err + } + + var ableToCast bool + result, ableToCast := rawResult.(types.DiskStat) + if !ableToCast { + return nil, errors.Errorf(types.ErrNamespaceCastResultFmt, result, rawResult) + } + return &result, nil +} diff --git a/vendor/github.com/longhorn/go-common-libs/ns/filelock.go b/vendor/github.com/longhorn/go-common-libs/ns/filelock.go new file mode 100644 index 00000000..b0ce92c6 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/ns/filelock.go @@ -0,0 +1,143 @@ +package ns + +import ( + "context" + "os" + "sync" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + csync "github.com/longhorn/go-common-libs/sync" + "github.com/longhorn/go-common-libs/types" +) + +// LockFile switches to the host namespace and locks a file at the specified path. +// It returns the file handle. +func LockFile(path string) (result *os.File, err error) { + defer func() { + err = errors.Wrapf(err, "failed to lock file %s", path) + }() + + fn := func() (interface{}, error) { + return csync.LockFile(path) + } + + rawResult, err := RunFunc(fn, 0) + if err != nil { + return nil, err + } + + var ableToCast bool + result, ableToCast = rawResult.(*os.File) + if !ableToCast { + return nil, errors.Errorf(types.ErrNamespaceCastResultFmt, result, rawResult) + } + return result, nil +} + +// FileLock is a struct responsible for locking a file. +type FileLock struct { + FilePath string // The path of the file to lock. + File *os.File // The file handle acquired after successful lock. + Timeout time.Duration // The maximum time to wait for lock acquisition. + + done chan struct{} // A channel for signaling lock release. + mutex *sync.Mutex // Mutex to prevent concurrent access to the file handle. +} + +// NewLock creates a new FileLock instance. +func NewLock(filepath string, timeout time.Duration) *FileLock { + if timeout == 0 { + timeout = types.FileLockDefaultTimeout + } + + return &FileLock{ + FilePath: filepath, + Timeout: timeout, + done: make(chan struct{}), + mutex: &sync.Mutex{}, + } +} + +// Lock locks a file. It starts a goroutine to lock the file and returns the file +// handle. If the lock acquisition exceeds the specified timeout, the function +// unlocks the file and returns an error. +// It also starts another goroutine to wait for lock to release and unlock the file. +func (lock *FileLock) Lock() error { + var err error + defer func() { + err = errors.Wrapf(err, "failed to lock file %s", lock.FilePath) + }() + + log := logrus.WithField("file", lock.FilePath) + + // Use a buffered channel for error handling to prevent goroutine leak. + errCh := make(chan error, 1) + + // Use a buffered channel for signaling successful lock acquisition. + resultCh := make(chan struct{}, 1) + + // Use a context with timeout for handling the lock timeout. + ctx, cancel := context.WithTimeout(context.Background(), lock.Timeout) + defer cancel() + + go func() { + lock.mutex.Lock() + defer lock.mutex.Unlock() + + result, err := LockFile(lock.FilePath) + if err != nil { + errCh <- err + return + } + + lock.File = result + resultCh <- struct{}{} + }() + + select { + case <-resultCh: + log.Trace("Locked file") + case <-ctx.Done(): + log.Trace("Timeout waiting for file to lock") + + lock.mutex.Lock() + defer lock.mutex.Unlock() + + if lock.File != nil { + err := csync.UnlockFile(lock.File) + if err != nil { + return errors.Wrapf(err, "failed to unlock timed out lock file %v", lock.FilePath) + } + lock.File = nil + } + + return errors.Errorf("timed out waiting for file to lock %v", lock.FilePath) + } + + // Wait for unlock + go func() { + <-lock.done + log.Trace("Received done signal to unlock file") + + lock.mutex.Lock() + defer lock.mutex.Unlock() + + if lock.File != nil { + err := csync.UnlockFile(lock.File) + if err != nil { + logrus.WithError(err).Error("Failed to gracefully unlock file") + } + lock.File = nil + + } + }() + return nil +} + +// Unlock closes the done channel to signal the lock to release. +func (lock *FileLock) Unlock() { + close(lock.done) +} diff --git a/vendor/github.com/longhorn/go-common-libs/ns/joiner.go b/vendor/github.com/longhorn/go-common-libs/ns/joiner.go new file mode 100644 index 00000000..b62e9c9b --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/ns/joiner.go @@ -0,0 +1,379 @@ +package ns + +import ( + "context" + "fmt" + "os" + "path/filepath" + "runtime" + "sync" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" + + "github.com/longhorn/go-common-libs/proc" + "github.com/longhorn/go-common-libs/types" + "github.com/longhorn/go-common-libs/utils" +) + +type Joiners []*Joiner + +// Joiner is a context with information about a namespace. +type Joiner struct { + namespace types.Namespace // The namespace to join (e.g. net, mnt) + fd int // The file descriptor of the namespace. + flags uintptr // The flags to use when joining the namespace. + + isJoined bool // A boolean to indicate if the namespace has been joined. +} + +// ReverseOrder returns a reversed copy of the Joiners. +func (joiners *Joiners) ReverseOrder() Joiners { + joinerCount := len(*joiners) + + reversed := make(Joiners, joinerCount) + for i, j := joinerCount-1, 0; i >= 0; i, j = i-1, j+1 { + reversed[j] = (*joiners)[i] + } + return reversed +} + +// JoinReverse joins all the namespaces in the Joiners in reverse order. +func (joiners *Joiners) JoinReverse() (err error) { + *joiners = joiners.ReverseOrder() + return joiners.Join() +} + +// Join joins all the namespaces in the Joiners. +func (joiners *Joiners) Join() (err error) { + for _, joiner := range *joiners { + if joiner.isJoined { + logrus.Tracef("Already joined namespace: %s", joiner.namespace) + continue + } + + if joiner.namespace == types.NamespaceMnt { + err := unix.Unshare(unix.CLONE_NEWNS) + if err != nil { + return errors.Wrapf(err, "failed to unshare namespace: %+s", joiner.namespace) + } + } + + if err := unix.Setns(joiner.fd, 0); err != nil { + return errors.Wrapf(err, "failed to set namespace: %+s", joiner.namespace) + } + + joiner.isJoined = true + logrus.Tracef("Joined namespace: %v", joiner.namespace) + } + return nil +} + +// Reset resets all the Joiners. +func (joiners *Joiners) Reset() (err error) { + for _, joiner := range *joiners { + logrus.Tracef("Resetting namespace: %+v", joiner) + joiner = &Joiner{} // nolint:ineffassign + } + return nil +} + +// OpenFile opens a file in the Joiners. +func (joiners *Joiners) OpenFile(path string) (fd int, err error) { + return unix.Open(path, unix.O_RDONLY|unix.O_CLOEXEC, 0) +} + +// CloseFiles closes all the files in the Joiners. +func (joiners *Joiners) CloseFiles() { + for _, joiner := range *joiners { + if joiner.fd == -1 { + continue + } + + if err := unix.Close(joiner.fd); err != nil { + logrus.WithError(err).Warnf("Failed to close %v namespace file", joiner.namespace) + continue + } + + joiner.fd = -1 + } +} + +// JoinerDescriptor is a struct that holds information about the namespaces to join. +type JoinerDescriptor struct { + namespaces []types.Namespace // List of namespaces to be joined. + + directory string // The target directory where the namespace files are located. + pid uint64 // Process ID associated with the Joiner. + + origin Joiners // Contexts of the original namespaces. + target Joiners // Contexts of the namespaces to be joined. + + stop chan struct{} // Channel to signal stopping the execution of the function within namespaces. + timeout time.Duration // Timeout duration for the execution of the function within namespaces. +} + +// RunFunc runs the given function in the host namespace. +// Returns the result of the function and any error that occurred. +func RunFunc(fn func() (interface{}, error), timeout time.Duration) (interface{}, error) { + joiner, err := NewJoiner(types.HostProcDirectory, 0) + if err != nil { + return nil, err + } + + return joiner.Run(fn) +} + +type JoinerInterface interface { + Revert() error + Run(fn func() (interface{}, error)) (interface{}, error) +} + +type NewJoinerFunc func(string, time.Duration) (JoinerInterface, error) + +// NewJoiner is a variable holding the function responsible for creating +// a new JoinerInterface. +// By using a variable for the creation function, it allows for easier unit testing +// by substituting a mock implementation. +var NewJoiner NewJoinerFunc = newJoiner + +// newJoiner creates a new JoinerInterface. +func newJoiner(procDirectory string, timeout time.Duration) (nsjoin JoinerInterface, err error) { + log := logrus.WithFields(logrus.Fields{ + "procDirectory": procDirectory, + "timeout": timeout, + }) + log.Trace("Initializing new namespace joiner") + + if timeout == 0 { + timeout = types.NsJoinerDefaultTimeout + } + + if procDirectory == "" { + return &JoinerDescriptor{ + stop: make(chan struct{}), + timeout: timeout, + }, nil + } + + nsDir := proc.GetHostNamespaceDirectory(procDirectory) + procPid := proc.GetHostNamespacePID(procDirectory) + + return &JoinerDescriptor{ + directory: nsDir, + pid: procPid, + + namespaces: []types.Namespace{ + types.NamespaceMnt, + types.NamespaceNet, + }, + + origin: Joiners{}, + target: Joiners{}, + + stop: make(chan struct{}), + timeout: timeout, + }, err +} + +// OpenNamespaceFiles opens required namespace files. +func (jd *JoinerDescriptor) OpenNamespaceFiles() (err error) { + defer func() { + err = errors.Wrapf(err, "failed to open namespace files") + if err != nil { + _ = jd.Revert() + } + }() + + for _, namespace := range jd.namespaces { + err = jd.openAndRecordNamespaceFiles(namespace) + if err != nil { + break + } + } + return err +} + +// openAndRecordNamespaceFiles opens and records current and target namespace files. +func (jd *JoinerDescriptor) openAndRecordNamespaceFiles(namespace types.Namespace) error { + logrus.Tracef("Opening %s namespace file", namespace) + + ns := namespace.String() + + if err := jd.openAndRecordOriginalNamespaceFile(ns, namespace); err != nil { + return err + } + + return jd.openAndRecordTargetNamespaceFile(ns, namespace) +} + +// openAndRecordOriginalNamespaceFile opens the original namespace file and records +// the file descriptor and namespace information. +// The original namespace file is the namespace file of the process thread that is +// executing the joiner (e.g. /proc/1/task/2/ns/mnt) +func (jd *JoinerDescriptor) openAndRecordOriginalNamespaceFile(ns string, namespace types.Namespace) error { + pthreadFile := filepath.Join("/proc", fmt.Sprint(os.Getpid()), "task", fmt.Sprint(unix.Gettid()), "ns", ns) + originFd, err := jd.origin.OpenFile(pthreadFile) + if err != nil { + return errors.Wrapf(err, "failed to open process thread file %v", pthreadFile) + } + jd.origin = append(jd.origin, &Joiner{ + namespace: namespace, + fd: originFd, + flags: namespace.Flag(), + }) + return nil +} + +// openAndRecordTargetNamespaceFile opens the target namespace file and records +// the file descriptor and namespace information. +// The target namespace file is the namespace file of the process that is being +// joined (e.g. /host/proc/123/ns/mnt) +func (jd *JoinerDescriptor) openAndRecordTargetNamespaceFile(ns string, namespace types.Namespace) error { + namespaceFile := filepath.Join(jd.directory, ns) + targetFd, err := jd.target.OpenFile(namespaceFile) + if err != nil { + return errors.Wrapf(err, "failed to open namespace file %v", namespaceFile) + } + jd.target = append(jd.target, &Joiner{ + namespace: namespace, + fd: targetFd, + flags: namespace.Flag(), + }) + return nil +} + +// Join joins the target namespaces. +func (jd *JoinerDescriptor) Join() (err error) { + defer func() { + err = errors.Wrap(err, "failed to join namespaces") + }() + return jd.target.Join() +} + +// Revert reverts to the original namespaces. +func (jd *JoinerDescriptor) Revert() (err error) { + if jd.target == nil { + return nil + } + + defer func() { + err = errors.Wrap(err, "failed to revert namespaces") + if err == nil { + logrus.Tracef("Reverted to %v namespace", jd.directory) + } + + jd.target.CloseFiles() + _ = jd.target.Reset() + + jd.origin.CloseFiles() + _ = jd.origin.Reset() + }() + + logrus.Trace("Reverting namespaces") + if err := jd.origin.JoinReverse(); err != nil { + return err + } + + _, err = os.Stat(jd.directory) + return err +} + +// Run executes the function in the target namespace. +// The function is executed in a goroutine with a locked OS thread to ensure +// namespace isolation. +func (jd *JoinerDescriptor) Run(fn func() (interface{}, error)) (interface{}, error) { + errCh := make(chan error) + resultCh := make(chan interface{}) + + _, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Use a wait group to wait for the goroutine to finish + var wg sync.WaitGroup + wg.Add(1) + + // Goroutine to run the function in the target namespace. + go func() { + defer wg.Done() + + defer close(errCh) + defer close(resultCh) + defer cancel() + + // The goroutine runs with a locked OS thread to ensure namespace isolation. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // Open the namespace files and revert them after execution. + err := jd.OpenNamespaceFiles() + if err != nil { + errCh <- err + return + } + defer func() { + _ = jd.Revert() + }() + + // Join the target namespaces. + logrus.Trace("Joining target namespaces") + err = jd.Join() + if err != nil { + errCh <- err + return + } + + // Execute the given function and send the result or error to the channels. + select { + case <-jd.stop: + return // The stop signal was received, exit without running the function. + default: + output, err := fn() + if err != nil { + errCh <- err + return + } + + resultCh <- output + } + }() + + // Goroutine to wait for the function to finish and stop the joiner. + var stopOnce sync.Once + go func() { + wg.Wait() + + // Stop the joiner once to avoid race conditions. + stopOnce.Do(func() { + close(jd.stop) + }) + }() + + // Get the function name for logging. + fnName := utils.GetFunctionName(fn) + + select { + case err := <-errCh: + return nil, errors.Wrapf(err, types.ErrNamespaceFuncFmt, fnName) + + case result := <-resultCh: + logrus.Tracef("Completed function %v in namespace: %+v", fnName, result) + return result, nil + + case <-jd.stop: + logrus.Tracef("Received stop signal while running function: %v", fnName) + stopOnce.Do(func() { + close(jd.stop) + }) + return nil, nil + + case <-time.After(jd.timeout): + // The function execution timed out, clean up and return an error. + stopOnce.Do(func() { + close(jd.stop) + }) + return nil, errors.Errorf("timeout running function: %v", fnName) + } +} diff --git a/vendor/github.com/longhorn/go-common-libs/ns/misc.go b/vendor/github.com/longhorn/go-common-libs/ns/misc.go new file mode 100644 index 00000000..c590abe8 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/ns/misc.go @@ -0,0 +1,23 @@ +package ns + +import ( + "github.com/sirupsen/logrus" + + "github.com/longhorn/go-common-libs/types" +) + +// GetDefaultProcessName returns the default process name for namespace switching based on the OS distro. +func GetDefaultProcessName() string { + osDistro, err := GetOSDistro() + if err != nil { + logrus.Trace("failed to get os distro, fallback to default host process") + return types.ProcessNone + } + + switch osDistro { + case types.OSDistroTalosLinux: + return types.ProcessKubelet + default: + return types.ProcessNone + } +} diff --git a/vendor/github.com/longhorn/go-common-libs/ns/sys.go b/vendor/github.com/longhorn/go-common-libs/ns/sys.go new file mode 100644 index 00000000..ff8b5616 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/ns/sys.go @@ -0,0 +1,99 @@ +package ns + +import ( + "syscall" + + "github.com/pkg/errors" + + "github.com/longhorn/go-common-libs/io" + "github.com/longhorn/go-common-libs/sys" + "github.com/longhorn/go-common-libs/types" +) + +// GetKernelRelease switches to the host namespace and retrieves the kernel release. +func GetKernelRelease() (string, error) { + var err error + defer func() { + err = errors.Wrap(err, "failed to get kernel release") + }() + + fn := func() (interface{}, error) { + return sys.GetKernelRelease() + } + + rawResult, err := RunFunc(fn, 0) + if err != nil { + return "", err + } + + var result string + var ableToCast bool + result, ableToCast = rawResult.(string) + if !ableToCast { + return "", errors.Errorf(types.ErrNamespaceCastResultFmt, result, rawResult) + } + return result, nil +} + +// GetOSDistro switches to the host namespace and retrieves the OS distro. +func GetOSDistro() (result string, err error) { + defer func() { + err = errors.Wrapf(err, "failed to get host OS distro") + }() + + fn := func() (interface{}, error) { + return io.ReadFileContent(types.OsReleaseFilePath) + } + + rawResult, err := RunFunc(fn, 0) + if err != nil { + return "", err + } + + var ableToCast bool + result, ableToCast = rawResult.(string) + if !ableToCast { + return "", errors.Errorf(types.ErrNamespaceCastResultFmt, result, rawResult) + } + + return sys.GetOSDistro(result) +} + +// Sync switches to the host namespace and calls sync. +func Sync() (err error) { + defer func() { + err = errors.Wrap(err, "failed to get kernel release") + }() + + fn := func() (interface{}, error) { + syscall.Sync() + return nil, nil + } + + _, err = RunFunc(fn, 0) + return err +} + +// GetSystemBlockDevices switches to the host namespace and retrieves the +// system block devices. +func GetSystemBlockDevices() (result map[string]types.BlockDeviceInfo, err error) { + defer func() { + err = errors.Wrapf(err, "failed to get system block devices") + }() + + fn := func() (interface{}, error) { + return sys.GetSystemBlockDeviceInfo() + } + + rawResult, err := RunFunc(fn, 0) + if err != nil { + return nil, err + } + + var ableToCast bool + result, ableToCast = rawResult.(map[string]types.BlockDeviceInfo) + if !ableToCast { + return nil, errors.Errorf(types.ErrNamespaceCastResultFmt, result, rawResult) + } + return result, nil +} diff --git a/vendor/github.com/longhorn/go-common-libs/proc/proc.go b/vendor/github.com/longhorn/go-common-libs/proc/proc.go new file mode 100644 index 00000000..581b7ec5 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/proc/proc.go @@ -0,0 +1,210 @@ +package proc + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/c9s/goprocinfo/linux" + "github.com/mitchellh/go-ps" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/longhorn/go-common-libs/types" +) + +// ProcessFinder is a struct to find process information. +type ProcessFinder struct { + procDirectory string // The directory path where the process information is stored. +} + +func NewProcFinder(procDir string) *ProcessFinder { + return &ProcessFinder{procDir} +} + +// GetProcessStatus returns the process status for the given process. +func (p *ProcessFinder) GetProcessStatus(proc string) (*linux.ProcessStatus, error) { + path := filepath.Join(p.procDirectory, proc, "status") + return linux.ReadProcessStatus(path) +} + +// FindAncestorByName returns the ancestor process status for the given process. +func (p *ProcessFinder) FindAncestorByName(ancestorProcess, pid string) (*linux.ProcessStatus, error) { + ps, err := p.GetProcessStatus(pid) + if err != nil { + return nil, err + } + + for { + if ps.Name == ancestorProcess { + return ps, nil + } + + if ps.PPid == 0 { + break + } + + ps, err = p.GetProcessStatus(fmt.Sprint(ps.PPid)) + if err != nil { + return nil, err + } + } + + return nil, errors.Errorf("failed to find the ancestor process for %v", ancestorProcess) +} + +// GetProcessPIDs returns the PIDs for the given process name. +func GetProcessPIDs(processName, procDir string) ([]uint64, error) { + files, err := os.ReadDir(procDir) + if err != nil { + return nil, err + } + + processFinder := NewProcFinder(procDir) + + var pids []uint64 + for _, file := range files { + if !file.IsDir() { + continue + } + + if _, err := strconv.Atoi(file.Name()); err != nil { + // Not a numerical values representing a running process + continue + } + + pid, err := strconv.ParseUint(file.Name(), 10, 64) + if err != nil { + // Not a numerical values representing a running process + continue + } + + processStatus, err := processFinder.GetProcessStatus(file.Name()) + if err != nil { + logrus.WithError(err).Debugf("Failed to get PID (%v) status", pid) + continue + } + + // The "Name" field of the process status is set to the filename of the executable. + // Within the proc directory structure, processes are organized by + // their PIDs. This means that multiple processes running with the same executable + // name can coexist. + if processStatus.Name == processName { + pids = append(pids, pid) + } + } + + // If no process is found, return the host namespace PID + if len(pids) == 0 { + pids = append(pids, GetHostNamespacePID(types.HostProcDirectory)) + } + + logrus.Tracef("Found PIDs (%v) for process %v", pids, processName) + return pids, nil +} + +// GetHostNamespacePID returns the PID of the host namespace. +func GetHostNamespacePID(hostProcDir string) uint64 { + pf := NewProcFinder(hostProcDir) + processes := []string{ + types.ProcessDockerd, + types.ProcessContainerd, + types.ProcessContainerdShim, + } + for _, process := range processes { + proc, err := pf.FindAncestorByName(process, types.ProcessSelf) + if err != nil { + continue + } + return proc.Pid + + } + // fall back to use pid 1 + return 1 +} + +// GetNamespaceDirectory returns the namespace directory for the given PID. +func GetNamespaceDirectory(procDir, pid string) string { + return filepath.Join(procDir, pid, "ns") +} + +// GetHostNamespaceDirectory returns the namespace directory for the host namespace. +func GetHostNamespaceDirectory(hostProcDir string) string { + return GetNamespaceDirectory(hostProcDir, fmt.Sprint(GetHostNamespacePID(hostProcDir))) +} + +// GetProcessAncestorNamespaceDirectory returns the namespace directory for the +// ancestor of the given process. +func GetProcessAncestorNamespaceDirectory(process, procDir string) (string, error) { + pf := NewProcFinder(procDir) + proc, err := pf.FindAncestorByName(process, types.ProcessSelf) + if err != nil { + return "", errors.Errorf("failed to get ancestor namespace of %v", process) + } + return GetNamespaceDirectory(procDir, fmt.Sprint(proc.Pid)), nil +} + +// GetProcessNamespaceDirectory returns the namespace directory for the given process. +// If processName is ProcessNone, it returns the host namespace directory. +func GetProcessNamespaceDirectory(processName, procDir string) (string, error) { + if processName == types.ProcessNone { + return GetHostNamespaceDirectory(procDir), nil + } + + pids, err := GetProcessPIDs(processName, procDir) + if err != nil { + return "", err + } + + return GetNamespaceDirectory(procDir, fmt.Sprint(pids[0])), nil +} + +// FindProcessByName finds a process by name and returns the process +func FindProcessByName(name string) (*os.Process, error) { + processes, err := ps.Processes() + if err != nil { + return nil, fmt.Errorf("failed to list processes") + } + + for _, process := range processes { + if process.Executable() == name { + return os.FindProcess(process.Pid()) + } + } + + return nil, fmt.Errorf("process %s is not found", name) +} + +// FindProcessByCmdline finds the processes with matching cmdline +func FindProcessByCmdline(cmdline string) ([]*os.Process, error) { + processes, err := ps.Processes() + if err != nil { + return nil, errors.Wrap(err, "failed to list processes") + } + + var foundProcesses []*os.Process + + for _, process := range processes { + cmdlinePath := filepath.Join("/proc", strconv.Itoa(process.Pid()), "cmdline") + cmdlineContent, err := os.ReadFile(cmdlinePath) + if err != nil { + continue + } + + if strings.HasPrefix(string(cmdlineContent), cmdline) { + process, err := os.FindProcess(process.Pid()) + if err != nil { + return nil, err + } + foundProcesses = append(foundProcesses, process) + } + } + + if len(foundProcesses) == 0 { + return nil, fmt.Errorf("process with cmdline %s is not found", cmdline) + } + + return foundProcesses, nil +} diff --git a/vendor/github.com/longhorn/go-common-libs/sync/filelock.go b/vendor/github.com/longhorn/go-common-libs/sync/filelock.go new file mode 100644 index 00000000..16eb8f79 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/sync/filelock.go @@ -0,0 +1,50 @@ +package sync + +import ( + "os" + "syscall" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// LockFile is responsible for locking a file and returning a file handle. +func LockFile(filePath string) (file *os.File, err error) { + log := logrus.WithField("file", filePath) + + defer func() { + err = errors.Wrapf(err, "failed to lock file %s", filePath) + if err != nil && file != nil { + e := file.Close() + if e != nil { + err = errors.Wrapf(err, "failed to close file: %v", e) + } + } + }() + + log.Trace("Locking file") + + file, err = os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return nil, err + } + + return file, syscall.Flock(int(file.Fd()), syscall.LOCK_EX) +} + +// UnlockFile is responsible for unlocking a file and closing the file handle. +func UnlockFile(file *os.File) (err error) { + defer func() { + err = errors.Wrapf(err, "failed to unlock file %s", file.Name()) + }() + + log := logrus.WithField("file", file.Name()) + + err = syscall.Flock(int(file.Fd()), syscall.LOCK_UN) + if err != nil { + log.WithError(err).Warn("Failed to unlock file") + } + + log.Trace("Unlocked file") + return file.Close() +} diff --git a/vendor/github.com/longhorn/go-common-libs/sys/sys.go b/vendor/github.com/longhorn/go-common-libs/sys/sys.go new file mode 100644 index 00000000..e4e23a30 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/sys/sys.go @@ -0,0 +1,129 @@ +package sys + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/longhorn/go-common-libs/types" +) + +// GetKernelRelease returns the kernel release string. +func GetKernelRelease() (string, error) { + utsname := &syscall.Utsname{} + if err := syscall.Uname(utsname); err != nil { + logrus.WithError(err).Warn("Failed to get kernel release") + return "", err + } + + // Extract the kernel release from the Utsname structure + release := make([]byte, 0, len(utsname.Release)) + for _, b := range utsname.Release { + if b == 0x00 { + logrus.Trace("Found end of kernel release string [0x00]") + break + } + release = append(release, byte(b)) + } + return string(release), nil +} + +// GetOSDistro reads the /etc/os-release file and returns the ID field. +func GetOSDistro(osReleaseContent string) (string, error) { + var err error + defer func() { + err = errors.Wrapf(err, "failed to get host OS distro") + }() + + lines := strings.Split(osReleaseContent, "\n") + for _, line := range lines { + if strings.HasPrefix(line, "ID=") { + id := strings.TrimPrefix(line, "ID=") + id = strings.Trim(id, `"`) + logrus.Tracef("Found OS distro: %v", id) + return id, nil + } + } + + return "", errors.Errorf("failed to find ID field in %v", types.OsReleaseFilePath) +} + +// GetSystemBlockDeviceInfo returns the block device info for the system. +func GetSystemBlockDeviceInfo() (map[string]types.BlockDeviceInfo, error) { + return getSystemBlockDeviceInfo(types.SysClassBlockDirectory, os.ReadDir, os.ReadFile) +} + +// getSystemBlockDeviceInfo returns the block device info for the system. +// It injects the readDirFn and readFileFn for testing. +func getSystemBlockDeviceInfo(sysClassBlockDirectory string, readDirFn func(string) ([]os.DirEntry, error), readFileFn func(string) ([]byte, error)) (map[string]types.BlockDeviceInfo, error) { + devices, err := readDirFn(sysClassBlockDirectory) + if err != nil { + return nil, err + } + + readDeviceNumber := func(numbers []string, index int) (int64, error) { + if len(numbers) <= index { + return 0, errors.Errorf("invalid file format") + } + + number, err := strconv.ParseInt(numbers[index], 10, 64) + if err != nil { + return 0, err + } + return number, nil + } + + deviceInfo := make(map[string]types.BlockDeviceInfo, len(devices)) + for _, device := range devices { + deviceName := device.Name() + devicePath := filepath.Join(sysClassBlockDirectory, deviceName, "dev") + + if _, err := os.Stat(devicePath); os.IsNotExist(err) { + // If the device path does not exist, check if the device path exists in the "device" directory. + // Some devices such as "nvme0cn1" created from SPDK do not have "dev" file under their sys/class/block directory. + alternativeDevicePath := filepath.Join(sysClassBlockDirectory, deviceName, "device", "dev") + if _, altErr := os.Stat(alternativeDevicePath); os.IsNotExist(altErr) { + errs := fmt.Errorf("primary error: %w; alternative error: %w", err, altErr) + logrus.WithFields(logrus.Fields{ + "device": deviceName, + "primaryPath": devicePath, + "alternativePath": alternativeDevicePath, + }).WithError(errs).Debugf("failed to find dev file in either primary or alternative path") + continue + } + + devicePath = alternativeDevicePath + } + + data, err := readFileFn(devicePath) + if err != nil { + return nil, err + } + + numbers := strings.Split(strings.TrimSpace(string(data)), ":") + major, err := readDeviceNumber(numbers, 0) + if err != nil { + logrus.WithError(err).Warnf("failed to read device %s major", deviceName) + continue + } + + minor, err := readDeviceNumber(numbers, 1) + if err != nil { + logrus.WithError(err).Warnf("failed to read device %s minor", deviceName) + continue + } + + deviceInfo[deviceName] = types.BlockDeviceInfo{ + Name: deviceName, + Major: int(major), + Minor: int(minor), + } + } + return deviceInfo, nil +} diff --git a/vendor/github.com/longhorn/go-common-libs/types/crypto.go b/vendor/github.com/longhorn/go-common-libs/types/crypto.go new file mode 100644 index 00000000..e80df9c1 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/types/crypto.go @@ -0,0 +1,16 @@ +package types + +import ( + "time" +) + +const ( + CryptoKeyProvider = "CRYPTO_KEY_PROVIDER" + CryptoKeyValue = "CRYPTO_KEY_VALUE" + CryptoKeyCipher = "CRYPTO_KEY_CIPHER" + CryptoKeyHash = "CRYPTO_KEY_HASH" + CryptoKeySize = "CRYPTO_KEY_SIZE" + CryptoPBKDF = "CRYPTO_PBKDF" +) + +const LuksTimeout = time.Minute diff --git a/vendor/github.com/longhorn/go-common-libs/types/exec.go b/vendor/github.com/longhorn/go-common-libs/types/exec.go new file mode 100644 index 00000000..93d1652d --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/types/exec.go @@ -0,0 +1,15 @@ +package types + +import ( + "time" +) + +const ( + BinaryCryptsetup = "cryptsetup" + BinaryFstrim = "fstrim" +) + +const ( + ExecuteNoTimeout = time.Duration(-1) + ExecuteDefaultTimeout = time.Minute +) diff --git a/vendor/github.com/longhorn/go-common-libs/types/file.go b/vendor/github.com/longhorn/go-common-libs/types/file.go new file mode 100644 index 00000000..e67b01dd --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/types/file.go @@ -0,0 +1,33 @@ +package types + +import ( + "time" +) + +var FileLockDefaultTimeout = 24 * time.Hour + +type DiskDriver string + +const ( + DiskDriverNone = DiskDriver("") + DiskDriverAuto = DiskDriver("auto") + DiskDriverAio = DiskDriver("aio") + DiskDriverNvme = DiskDriver("nvme") + DiskDriverVirtioScsi = DiskDriver("virtio-scsi") + DiskDriverVirtioBlk = DiskDriver("virtio-blk") + DiskDriverVirtioPci = DiskDriver("virtio-pci") + DiskDriverUioPciGeneric = DiskDriver("uio_pci_generic") +) + +type DiskStat struct { + DiskID string + Name string + Path string + Type string + Driver DiskDriver + FreeBlocks int64 + TotalBlocks int64 + BlockSize int64 + StorageMaximum int64 + StorageAvailable int64 +} diff --git a/vendor/github.com/longhorn/go-common-libs/types/misc.go b/vendor/github.com/longhorn/go-common-libs/types/misc.go new file mode 100644 index 00000000..083d53bd --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/types/misc.go @@ -0,0 +1,3 @@ +package types + +const RandomIDDefaultLength = 8 diff --git a/vendor/github.com/longhorn/go-common-libs/types/namespace.go b/vendor/github.com/longhorn/go-common-libs/types/namespace.go new file mode 100644 index 00000000..7358a407 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/types/namespace.go @@ -0,0 +1,42 @@ +package types + +import ( + "time" + + "golang.org/x/sys/unix" +) + +const ( + HostProcDirectory = "/host/proc" + ProcDirectory = "/proc" +) + +const NsBinary = "nsenter" + +const ( + ErrNamespaceCastResultFmt = "failed casting result to %T: %v" + ErrNamespaceFuncFmt = "failed function: %v" +) + +var NsJoinerDefaultTimeout = 24 * time.Hour + +type Namespace string + +const ( + NamespaceIpc = Namespace("ipc") + NamespaceMnt = Namespace("mnt") + NamespaceNet = Namespace("net") +) + +func (ns Namespace) Flag() uintptr { + switch ns { + case NamespaceNet: + return unix.CLONE_NEWNET + default: + return 0 + } +} + +func (ns Namespace) String() string { + return string(ns) +} diff --git a/vendor/github.com/longhorn/go-common-libs/types/proc.go b/vendor/github.com/longhorn/go-common-libs/types/proc.go new file mode 100644 index 00000000..27aaa0a9 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/types/proc.go @@ -0,0 +1,12 @@ +package types + +const ( + ProcessContainerd = "containerd" + ProcessContainerdShim = "containerd-shim" + ProcessDockerd = "dockerd" + ProcessNone = "" + ProcessSelf = "self" + + // ProcessKubelet is not a standard process name for Kubelet, used by Talos Linux only. + ProcessKubelet = "kubelet" +) diff --git a/vendor/github.com/longhorn/go-common-libs/types/sys.go b/vendor/github.com/longhorn/go-common-libs/types/sys.go new file mode 100644 index 00000000..15155b3b --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/types/sys.go @@ -0,0 +1,13 @@ +package types + +const OsReleaseFilePath = "/etc/os-release" +const SysClassBlockDirectory = "/sys/class/block/" + +const OSDistroTalosLinux = "talos" + +// BlockDeviceInfo is a struct that contains the block device info. +type BlockDeviceInfo struct { + Name string // Name of the block device (e.g. sda, sdb, sdc, etc.). + Major int // Major number of the block device. + Minor int // Minor number of the block device. +} diff --git a/vendor/github.com/longhorn/go-common-libs/utils/grpc.go b/vendor/github.com/longhorn/go-common-libs/utils/grpc.go new file mode 100644 index 00000000..237a6ec4 --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/utils/grpc.go @@ -0,0 +1,17 @@ +package utils + +import ( + "strings" + "time" +) + +const ( + GRPCServiceTimeout = 3 * time.Minute +) + +func GetGRPCAddress(address string) string { + address = strings.TrimPrefix(address, "tcp://") + address = strings.TrimPrefix(address, "http://") + + return address +} diff --git a/vendor/github.com/longhorn/go-common-libs/utils/misc.go b/vendor/github.com/longhorn/go-common-libs/utils/misc.go new file mode 100644 index 00000000..69aced9c --- /dev/null +++ b/vendor/github.com/longhorn/go-common-libs/utils/misc.go @@ -0,0 +1,121 @@ +package utils + +import ( + "crypto/rand" + "fmt" + "math/big" + "path/filepath" + "reflect" + "runtime" + "strconv" + "strings" + + "github.com/google/uuid" + "github.com/pkg/errors" + + "github.com/longhorn/go-common-libs/types" +) + +// Contains checks if the given slice contains the given value. +func Contains[T comparable](slice []T, value T) bool { + for _, s := range slice { + if s == value { + return true + } + } + return false +} + +// GetFunctionName returns the . of the given function. +func GetFunctionName(f interface{}) string { + value := reflect.ValueOf(f) + if value.Kind() != reflect.Func { + return "" + } + return filepath.Base(runtime.FuncForPC(value.Pointer()).Name()) +} + +// GetFunctionPath returns the full path of the given function. +func GetFunctionPath(f interface{}) string { + getFn := func() string { + return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() + } + return GetFunctionInfo(f, getFn) +} + +// GetFunctionInfo take a function as interface{} and a function for getting the info. +func GetFunctionInfo(f interface{}, getInfoFn func() string) string { + value := reflect.ValueOf(f) + if value.Kind() != reflect.Func { + return "" + } + return getInfoFn() +} + +// IsStringInSlice checks if the given string 'item' is present in the 'list' of strings. +func IsStringInSlice(list []string, item string) bool { + for _, str := range list { + if str == item { + return true + } + } + return false +} + +// RandomID returns a random string with the specified length. +// If the specified length is less than or equal to 0, the default length will +// be used. +func RandomID(randomIDLenth int) string { + if randomIDLenth <= 0 { + randomIDLenth = types.RandomIDDefaultLength + } + + uuid := strings.Replace(UUID(), "-", "", -1) + + if len(uuid) > randomIDLenth { + uuid = uuid[:randomIDLenth] + } + return uuid +} + +// UUID returns a random UUID string. +func UUID() string { + return uuid.New().String() +} + +// GenerateRandomNumber generates a random positive number between lower and upper. +// The return value should be between [lower, upper), and error is nil when success. +// If the error is not nil, the return value is 0. +func GenerateRandomNumber(lower, upper int64) (int64, error) { + if lower > upper { + return 0, errors.Errorf("invalid boundary: [%v, %v)", lower, upper) + } + + if lower == upper { + return lower, nil + } + + randNum, err := rand.Int(rand.Reader, big.NewInt(upper-lower)) + if err != nil { + return 0, err + } + return (lower + randNum.Int64()), nil +} + +// ConvertTypeToString converts the given value to string. +func ConvertTypeToString[T any](value T) string { + v := reflect.ValueOf(value) + + switch v.Kind() { + case reflect.String: + return v.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(v.Int(), 10) + case reflect.Float32, reflect.Float64: + return strconv.FormatFloat(v.Float(), 'f', -1, 64) + case reflect.Bool: + return strconv.FormatBool(v.Bool()) + default: + return fmt.Sprintf("Unsupported type: %v", v.Kind()) + } +} diff --git a/vendor/github.com/mitchellh/go-ps/.gitignore b/vendor/github.com/mitchellh/go-ps/.gitignore new file mode 100644 index 00000000..a977916f --- /dev/null +++ b/vendor/github.com/mitchellh/go-ps/.gitignore @@ -0,0 +1 @@ +.vagrant/ diff --git a/vendor/github.com/mitchellh/go-ps/.travis.yml b/vendor/github.com/mitchellh/go-ps/.travis.yml new file mode 100644 index 00000000..8f794f71 --- /dev/null +++ b/vendor/github.com/mitchellh/go-ps/.travis.yml @@ -0,0 +1,4 @@ +language: go + +go: + - 1.2.1 diff --git a/vendor/github.com/mitchellh/go-ps/LICENSE.md b/vendor/github.com/mitchellh/go-ps/LICENSE.md new file mode 100644 index 00000000..22985159 --- /dev/null +++ b/vendor/github.com/mitchellh/go-ps/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/go-ps/README.md b/vendor/github.com/mitchellh/go-ps/README.md new file mode 100644 index 00000000..4e3d0e14 --- /dev/null +++ b/vendor/github.com/mitchellh/go-ps/README.md @@ -0,0 +1,34 @@ +# Process List Library for Go [![GoDoc](https://godoc.org/github.com/mitchellh/go-ps?status.png)](https://godoc.org/github.com/mitchellh/go-ps) + +go-ps is a library for Go that implements OS-specific APIs to list and +manipulate processes in a platform-safe way. The library can find and +list processes on Linux, Mac OS X, Solaris, and Windows. + +If you're new to Go, this library has a good amount of advanced Go educational +value as well. It uses some advanced features of Go: build tags, accessing +DLL methods for Windows, cgo for Darwin, etc. + +How it works: + + * **Darwin** uses the `sysctl` syscall to retrieve the process table. + * **Unix** uses the procfs at `/proc` to inspect the process tree. + * **Windows** uses the Windows API, and methods such as + `CreateToolhelp32Snapshot` to get a point-in-time snapshot of + the process table. + +## Installation + +Install using standard `go get`: + +``` +$ go get github.com/mitchellh/go-ps +... +``` + +## TODO + +Want to contribute? Here is a short TODO list of things that aren't +implemented for this library that would be nice: + + * FreeBSD support + * Plan9 support diff --git a/vendor/github.com/mitchellh/go-ps/Vagrantfile b/vendor/github.com/mitchellh/go-ps/Vagrantfile new file mode 100644 index 00000000..61662ab1 --- /dev/null +++ b/vendor/github.com/mitchellh/go-ps/Vagrantfile @@ -0,0 +1,43 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + config.vm.box = "chef/ubuntu-12.04" + + config.vm.provision "shell", inline: $script + + ["vmware_fusion", "vmware_workstation"].each do |p| + config.vm.provider "p" do |v| + v.vmx["memsize"] = "1024" + v.vmx["numvcpus"] = "2" + v.vmx["cpuid.coresPerSocket"] = "1" + end + end +end + +$script = <