diff --git a/.gitignore b/.gitignore index 56be033..ff0306a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # customize .cookie.yaml -harbor-go-client +harborctl* *.tar.gz diff --git a/CHANGELOG.md b/CHANGELOG.md index c8d6de5..04fabdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ + +# [1.0.0](https://github.com/moooofly/harbor-go-client/compare/v0.9.6...v1.0.0) (2018-10-24) + + +### Features + +* add replications trigger API ([0753d6c](https://github.com/moooofly/harbor-go-client/commit/0753d6c)) +* add syncregistry and email_ping APIs ([2b18258](https://github.com/moooofly/harbor-go-client/commit/2b18258)) +* add usergroups APIs ([47ef550](https://github.com/moooofly/harbor-go-client/commit/47ef550)) +* support both linux and darwin platform compilation ([a665e0f](https://github.com/moooofly/harbor-go-client/commit/a665e0f)) +* support darwin platform, fix [#1](https://github.com/moooofly/harbor-go-client/issues/1) ([c671c20](https://github.com/moooofly/harbor-go-client/commit/c671c20)) + + + # [0.9.6](https://github.com/moooofly/harbor-go-client/compare/v0.9.5...v0.9.6) (2018-10-17) diff --git a/Makefile b/Makefile index 00374c1..b60ea46 100644 --- a/Makefile +++ b/Makefile @@ -5,11 +5,14 @@ ifeq "$(GOPATH)" "" $(error Please set the environment variable GOPATH before running `make`) endif -GO := go -PKGS := $(shell $(GO) list ./... | grep -v vendor) +GOOS := $(shell go env GOOS) +GOARCH := $(shell go env GOARCH) +PKGS := $(shell go list ./... | grep -v vendor) + + # NOTE: '-race' requires cgo; enable cgo by setting CGO_ENABLED=1 #BUILD_FLAG := -race -GOBUILD := CGO_ENABLED=0 $(GO) build $(BUILD_FLAG) +GOBUILD := CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} go build $(BUILD_FLAG) LDFLAGS += -X "github.com/moooofly/harbor-go-client/utils.ClientVersion=$(shell cat VERSION)" LDFLAGS += -X "github.com/moooofly/harbor-go-client/utils.GoVersion=$(shell go version)" @@ -24,12 +27,12 @@ all: lint build test build: @echo "==> Building ..." - $(GOBUILD) -ldflags '$(LDFLAGS)' ./ + $(GOBUILD) -o harborctl_${GOOS}_${GOARCH} -ldflags '$(LDFLAGS)' ./ @echo "" install: @echo "==> Installing ..." - $(GO) install -x ${SRC} + go install -x ${SRC} @echo "" lint: @@ -43,19 +46,27 @@ lint: test: @echo "==> Testing ..." - $(GO) test -short -race $(PKGS) + go test -short -race $(PKGS) @echo "" -pack: build +build_linux: + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build $(BUILD_FLAG) -o harborctl_linux_amd64 -ldflags '$(LDFLAGS)' ./ + +build_darwin: + CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build $(BUILD_FLAG) -o harborctl_darwin_amd64 -ldflags '$(LDFLAGS)' ./ + +pack: build_linux build_darwin @echo "==> Packing ..." - @tar czvf $(shell cat VERSION)-bin.tar.gz harbor-go-client conf/*.yaml + @tar czvf harborctl-$(shell cat VERSION).linux-amd64.tar.gz harborctl_linux_amd64 conf/*.yaml @echo "" - @rm harbor-go-client + @tar czvf harborctl-$(shell cat VERSION).darwin-amd64.tar.gz harborctl_darwin_amd64 conf/*.yaml @echo "" + @rm harborctl_linux_amd64 + @rm harborctl_darwin_amd64 docker: @echo "==> Creating docker image ..." - docker build -t harbor-go-client:$(shell git rev-parse --short HEAD) . + docker build -t harborctl:$(shell git rev-parse --short HEAD) . @echo "" misspell: @@ -73,7 +84,8 @@ shellcheck: clean: @echo "==> Cleaning ..." - $(GO) clean -x -i ${SRC} + go clean -x -i ${SRC} + rm -f harborctl_* rm -rf *.out rm -rf *.tar.gz @echo "" diff --git a/README.md b/README.md index 9a5d2d1..85dbeb5 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ Current Harbor API support status: - [x] GET /api/labels/{id} - [x] PUT /api/labels/{id} - replications - - [ ] POST /api/replications + - [x] POST /api/replications - targets - [x] GET /api/targets - [x] POST /api/targets @@ -104,7 +104,7 @@ Current Harbor API support status: - [x] PUT /api/targets/{id} - [x] GET /api/targets/{id}/policies/ - internal - - [ ] POST /api/internal/syncregistry + - [x] POST /api/internal/syncregistry - systeminfo - [x] GET /api/systeminfo - [x] GET /api/systeminfo/volumes @@ -115,17 +115,17 @@ Current Harbor API support status: - [ ] GET /api/ldap/users/search - [ ] POST /api/ldap/users/import - usergroups - - [ ] GET /api/usergroups - - [ ] POST /api/usergroups - - [ ] DELETE /api/usergroups/{group_id} - - [ ] GET /api/usergroups/{group_id} - - [ ] PUT /api/usergroups/{group_id} + - [x] GET /api/usergroups + - [x] POST /api/usergroups + - [x] DELETE /api/usergroups/{group_id} + - [x] GET /api/usergroups/{group_id} + - [x] PUT /api/usergroups/{group_id} - configurations - [x] GET /api/configurations - [x] PUT /api/configurations - [x] POST /api/configurations/reset - email - - [ ] POST /api/email/ping + - [x] POST /api/email/ping Additional features supported: diff --git a/VERSION b/VERSION index c7ceed8..3eefcb9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.9.6 +1.0.0 diff --git a/api/others.go b/api/others.go index 3cb1c6c..b6cab43 100644 --- a/api/others.go +++ b/api/others.go @@ -1,4 +1,118 @@ package api -// POST /email/ping -// POST /internal/syncregistry +import ( + "encoding/json" + "fmt" + + "github.com/moooofly/harbor-go-client/utils" +) + +func init() { + utils.Parser.AddCommand("syncregistry", + "Sync repositories from registry to DB.", + "This endpoint is for syncing all repositories of registry with database.", + &syncregistry) + utils.Parser.AddCommand("email_ping", + "Test connection and authentication with email server.", + "Test connection and authentication with email server.", + &emailping) +} + +type syncRegistry struct { +} + +var syncregistry syncRegistry + +func (x *syncRegistry) Execute(args []string) error { + PostSyncRegistry(utils.URLGen("/api/internal/syncregistry")) + return nil +} + +// PostSyncRegistry is for syncing all repositories of registry with database. +// +// params: +// +// format: +// POST /internal/syncregistry +// +// e.g. curl -X POST --header 'Content-Type: application/json' --header 'Accept: text/plain' 'https://localhost/api/internal/syncregistry' +func PostSyncRegistry(baseURL string) { + targetURL := baseURL + fmt.Println("==> POST", targetURL) + + // Read beegosessionID from .cookie.yaml + c, err := utils.CookieLoad() + if err != nil { + fmt.Println("Error:", err) + return + } + + utils.Request.Post(targetURL). + Set("Cookie", "harbor-lang=zh-cn; beegosessionID="+c.BeegosessionID). + End(utils.PrintStatus) +} + +type emailPing struct { + EmailHost string `short:"h" long:"email_host" description:"The host of email server." default:"smtp.mydomain.com" json:"email_host"` + EmailPort int `short:"t" long:"email_port" description:"The port of email server." default:"25" json:"email_port"` + EmailUsername string `short:"u" long:"email_username" description:"The username of email server." default:"sample_admin@mydomain.com" json:"email_username"` + EmailPassword string `short:"p" long:"email_password" description:"The password of email server." default:"" json:"email_password"` + EmailSsl bool `short:"s" long:"email_ssl" description:"Use ssl/tls or not." json:"email_ssl"` + EmailIdentity string `short:"i" long:"email_identity" description:"The identity of email server." default:"" json:"email_identity"` +} + +var emailping emailPing + +func (x *emailPing) Execute(args []string) error { + PostEmailPing(utils.URLGen("/api/email/ping")) + return nil +} + +// PostEmailPing tests connection and authentication with email server. +// +// params: +// email_host - The host of email server. +// email_port - The port of email server. +// email_username - The username of email server. +// email_password - The password of email server. +// email_ssl - Use ssl/tls or not. +// email_identity - The dentity of email server. +// +// format: +// POST /email/ping +// +// e.g. +/* +curl -X POST --header 'Content-Type: application/json' --header 'Accept: text/plain' -d '{ \ + "email_host": "string", \ + "email_port": 0, \ + "email_username": "string", \ + "email_password": "string", \ + "email_ssl": true, \ + "email_identity": "string" \ + }' 'https://localhost/api/email/ping' +)*/ +func PostEmailPing(baseURL string) { + targetURL := baseURL + fmt.Println("==> POST", targetURL) + + // Read beegosessionID from .cookie.yaml + c, err := utils.CookieLoad() + if err != nil { + fmt.Println("Error:", err) + return + } + + t, err := json.Marshal(&emailping) + if err != nil { + fmt.Println("error:", err) + return + } + + fmt.Println("==> email ping:", string(t)) + + utils.Request.Post(targetURL). + Set("Cookie", "harbor-lang=zh-cn; beegosessionID="+c.BeegosessionID). + Send(string(t)). + End(utils.PrintStatus) +} diff --git a/api/replications.go b/api/replications.go new file mode 100644 index 0000000..bc366ff --- /dev/null +++ b/api/replications.go @@ -0,0 +1,63 @@ +package api + +import ( + "encoding/json" + "fmt" + + "github.com/moooofly/harbor-go-client/utils" +) + +func init() { + utils.Parser.AddCommand("replication_trigger_by_id", + "Trigger the replication according to the specified policy.", + "This endpoint is used to trigger a replication.", + &replTriByID) +} + +type replicationTriByID struct { + PolicyID int `short:"i" long:"policy_id" description:"(REQUIRED) The ID of replication policy" required:"yes" json:"policy_id"` +} + +var replTriByID replicationTriByID + +func (x *replicationTriByID) Execute(args []string) error { + PostReplTriByID(utils.URLGen("/api/replications")) + return nil +} + +// PostReplTriByID is used to trigger a replication. +// +// params: +// policy_id - (REQUIRED) The ID of replication policy +// +// format: +// POST /replications +// +// e.g. +/* + curl -X POST --header 'Content-Type: application/json' --header 'Accept: text/plain' -d '{ \ + "policy_id": 1 \ + }' 'https://localhost/api/replications' +*/ +func PostReplTriByID(baseURL string) { + targetURL := baseURL + fmt.Println("==> POST", targetURL) + + // Read beegosessionID from .cookie.yaml + c, err := utils.CookieLoad() + if err != nil { + fmt.Println("Error:", err) + return + } + + t, err := json.Marshal(&replTriByID) + if err != nil { + fmt.Println("error:", err) + return + } + + utils.Request.Post(targetURL). + Set("Cookie", "harbor-lang=zh-cn; beegosessionID="+c.BeegosessionID). + Send(string(t)). + End(utils.PrintStatus) +} diff --git a/api/usergroups.go b/api/usergroups.go new file mode 100644 index 0000000..76129f0 --- /dev/null +++ b/api/usergroups.go @@ -0,0 +1,241 @@ +package api + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/moooofly/harbor-go-client/utils" +) + +func init() { + utils.Parser.AddCommand("usergroups_list", + "Get all user groups information", + "Get all user groups information", + &ugList) + utils.Parser.AddCommand("usergroup_create", + "Create user group", + "Create user group information", + &ugCreate) + utils.Parser.AddCommand("usergroup_del", + "Delete user group", + "Delete user group", + &ugDel) + utils.Parser.AddCommand("usergroup_get", + "Get user group information", + "Get user group information", + &ugGet) + utils.Parser.AddCommand("usergroup_update", + "Update group information", + "Update group information", + &ugUpdate) +} + +type usergroupsList struct { +} + +var ugList usergroupsList + +func (x *usergroupsList) Execute(args []string) error { + GetUsergroupsList(utils.URLGen("/api/usergroups")) + return nil +} + +// GetUsergroupsList get all user groups information +// +// params: +// +// e.g. curl -X GET --header 'Accept: application/json' 'https://localhost/api/usergroups' +func GetUsergroupsList(baseURL string) { + targetURL := baseURL + fmt.Println("==> GET", targetURL) + + // Read beegosessionID from .cookie.yaml + c, err := utils.CookieLoad() + if err != nil { + fmt.Println("Error:", err) + return + } + + utils.Request.Get(targetURL). + Set("Cookie", "harbor-lang=zh-cn; beegosessionID="+c.BeegosessionID). + End(utils.PrintStatus) +} + +type usergroupCreate struct { + ID int `short:"i" long:"id" description:"The ID of the user group" default:"0" json:"id"` + GroupName string `short:"n" long:"group_name" description:"The name of the user group" default:"tmp-group" json:"group_name"` + GroupType int `short:"t" long:"group_type" description:"The group type, 1 for LDAP group." default:"1" json:"group_type"` + LDAPGroupDN string `short:"l" long:"ldap_group_dn" description:"The DN of the LDAP group if group type is 1 (LDAP group)." default:"" json:"ldap_group_dn"` +} + +var ugCreate usergroupCreate + +func (x *usergroupCreate) Execute(args []string) error { + PostUsergroupCreate(utils.URLGen("/api/usergroups")) + return nil +} + +// PostUsergroupCreate create user group information +// +// params: +// id - The ID of the user group +// group_name - The name of the user group +// group_type - The group type, 1 for LDAP group +// ldap_group_dn - The DN of the LDAP group if group type is 1 (LDAP group) +// +// e.g. +/* +curl -X POST --header 'Content-Type: application/json' --header 'Accept: text/plain' -d '{ \ + "id": 100, \ + "group_name": "tmp-group", \ + "group_type": 1, \ + "ldap_group_dn": "" \ + }' 'https://localhost/api/usergroups' +*/ +func PostUsergroupCreate(baseURL string) { + targetURL := baseURL + fmt.Println("==> POST", targetURL) + + // Read beegosessionID from .cookie.yaml + c, err := utils.CookieLoad() + if err != nil { + fmt.Println("Error:", err) + return + } + + t, err := json.Marshal(&ugCreate) + if err != nil { + fmt.Println("error:", err) + return + } + + fmt.Println("===> usergroup create:", string(t)) + + utils.Request.Post(targetURL). + Set("Cookie", "harbor-lang=zh-cn; beegosessionID="+c.BeegosessionID). + Send(string(t)). + End(utils.PrintStatus) +} + +type usergroupDel struct { + ID int `short:"i" long:"id" description:"(REQUIRED) The ID of the user group" required:"yes"` +} + +var ugDel usergroupDel + +func (x *usergroupDel) Execute(args []string) error { + DeleteUsergroup(utils.URLGen("/api/usergroups")) + return nil +} + +// DeleteUsergroup delete user group +// +// params: +// id - (REQUIRED) The ID of the user group +// +// e.g. curl -X DELETE --header 'Accept: text/plain' 'https://localhost/api/usergroups/1' +func DeleteUsergroup(baseURL string) { + targetURL := baseURL + "/" + strconv.Itoa(ugDel.ID) + fmt.Println("==> DELETE", targetURL) + + // Read beegosessionID from .cookie.yaml + c, err := utils.CookieLoad() + if err != nil { + fmt.Println("Error:", err) + return + } + + utils.Request.Delete(targetURL). + Set("Cookie", "harbor-lang=zh-cn; beegosessionID="+c.BeegosessionID). + End(utils.PrintStatus) +} + +type usergroupGet struct { + ID int `short:"i" long:"id" description:"(REQUIRED) The ID of the user group" required:"yes"` +} + +var ugGet usergroupGet + +func (x *usergroupGet) Execute(args []string) error { + GetUsergroup(utils.URLGen("/api/usergroups")) + return nil +} + +// GetUsergroup get user group information +// +// params: +// id - (REQUIRED) The ID of the user group +// +// e.g. curl -X GET --header 'Accept: text/plain' 'https://localhost/api/usergroups/1' +func GetUsergroup(baseURL string) { + targetURL := baseURL + "/" + strconv.Itoa(ugGet.ID) + fmt.Println("==> GET", targetURL) + + // Read beegosessionID from .cookie.yaml + c, err := utils.CookieLoad() + if err != nil { + fmt.Println("Error:", err) + return + } + + utils.Request.Get(targetURL). + Set("Cookie", "harbor-lang=zh-cn; beegosessionID="+c.BeegosessionID). + End(utils.PrintStatus) +} + +type usergroupUpdate struct { + ID int `short:"i" long:"id" description:"The ID of the user group" default:"0" json:"id"` + GroupName string `short:"n" long:"group_name" description:"The name of the user group" default:"tmp-group" json:"group_name"` + GroupType int `short:"t" long:"group_type" description:"The group type, 1 for LDAP group." default:"1" json:"group_type"` + LDAPGroupDN string `short:"l" long:"ldap_group_dn" description:"The DN of the LDAP group if group type is 1 (LDAP group)." default:"" json:"ldap_group_dn"` +} + +var ugUpdate usergroupUpdate + +func (x *usergroupUpdate) Execute(args []string) error { + PutUsergroup(utils.URLGen("/api/usergroups")) + return nil +} + +// PutUsergroup update user group information +// +// params: +// id - The ID of the user group +// group_name - The name of the user group +// group_type - The group type, 1 for LDAP group +// ldap_group_dn - The DN of the LDAP group if group type is 1 (LDAP group) +// +// e.g. +/* +curl -X PUT --header 'Content-Type: application/json' --header 'Accept: text/plain' -d '{ \ + "id": 1, \ + "group_name": "tmp-group", \ + "group_type": 1, \ + "ldap_group_dn": "" \ + }' 'https://localhost/api/usergroups/1' +*/ +func PutUsergroup(baseURL string) { + targetURL := baseURL + "/" + strconv.Itoa(ugUpdate.ID) + fmt.Println("==> PUT", targetURL) + + // Read beegosessionID from .cookie.yaml + c, err := utils.CookieLoad() + if err != nil { + fmt.Println("Error:", err) + return + } + + t, err := json.Marshal(&ugUpdate) + if err != nil { + fmt.Println("error:", err) + return + } + + fmt.Println("===> usergroup update:", string(t)) + + utils.Request.Put(targetURL). + Set("Cookie", "harbor-lang=zh-cn; beegosessionID="+c.BeegosessionID). + Send(string(t)). + End(utils.PrintStatus) +} diff --git a/utils/password_concealer.go b/utils/password_concealer.go index 0b4c4b9..985f3a7 100644 --- a/utils/password_concealer.go +++ b/utils/password_concealer.go @@ -2,72 +2,13 @@ package utils import ( "bufio" - "errors" "fmt" "io" "os" - "os/signal" - "syscall" - "unsafe" - "golang.org/x/sys/unix" + "github.com/moooofly/harbor-go-client/utils/term" ) -const ( - getTermios = unix.TCGETS - setTermios = unix.TCSETS -) - -var ( - // ErrInvalidState is returned if the state of the terminal is invalid. - ErrInvalidState = errors.New("Invalid terminal state") -) - -// Termios is the Unix API for terminal I/O. -type Termios unix.Termios - -// State represents the state of the terminal. -type State struct { - termios Termios -} - -func tcget(fd uintptr, p *Termios) syscall.Errno { - _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(p))) - return err -} - -func tcset(fd uintptr, p *Termios) syscall.Errno { - _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(p))) - return err -} - -// RestoreTerminal restores the terminal connected to the given file descriptor -// to a previous state. -func RestoreTerminal(fd uintptr, state *State) error { - if state == nil { - return ErrInvalidState - } - if err := tcset(fd, &state.termios); err != 0 { - return err - } - return nil -} - -func handleInterrupt(fd uintptr, state *State) { - sigchan := make(chan os.Signal, 1) - signal.Notify(sigchan, os.Interrupt) - go func() { - for range sigchan { - // quit cleanly and the new terminal item is on a new line - fmt.Println() - signal.Stop(sigchan) - close(sigchan) - RestoreTerminal(fd, state) - os.Exit(1) - } - }() -} - func readInput(in io.Reader, out io.Writer) string { reader := bufio.NewReader(in) line, _, err := reader.ReadLine() @@ -81,25 +22,23 @@ func readInput(in io.Reader, out io.Writer) string { // ReadPasswordFromTerm gets user password from stdin without showing on screen func ReadPasswordFromTerm() (string, error) { - var oldState State - if err := tcget(os.Stdin.Fd(), &oldState.termios); err != 0 { + oldState, err := term.SaveState(os.Stdin.Fd()) + if err != nil { return "", err } fmt.Fprintf(os.Stdout, "Password: ") - newState := oldState.termios - newState.Lflag &^= unix.ECHO - - if err := tcset(os.Stdin.Fd(), &newState); err != 0 { + err = term.DisableEcho(os.Stdin.Fd(), oldState) + if err != nil { return "", err } - handleInterrupt(os.Stdin.Fd(), &oldState) passwd := readInput(os.Stdin, os.Stdout) fmt.Fprint(os.Stdout, "\n") - if err := tcset(os.Stdin.Fd(), &oldState.termios); err != 0 { + err = term.RestoreTerminal(os.Stdin.Fd(), oldState) + if err != nil { return "", err } diff --git a/utils/term/tc.go b/utils/term/tc.go new file mode 100644 index 0000000..19dbb1c --- /dev/null +++ b/utils/term/tc.go @@ -0,0 +1,20 @@ +// +build !windows + +package term + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +func tcget(fd uintptr, p *Termios) syscall.Errno { + _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(p))) + return err +} + +func tcset(fd uintptr, p *Termios) syscall.Errno { + _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(p))) + return err +} diff --git a/utils/term/term.go b/utils/term/term.go new file mode 100644 index 0000000..b0448fb --- /dev/null +++ b/utils/term/term.go @@ -0,0 +1,74 @@ +// +build !windows + +// Package term provides structures and helper functions to work with +// terminal (state, sizes). +package term + +import ( + "errors" + "fmt" + "os" + "os/signal" + + "golang.org/x/sys/unix" +) + +var ( + // ErrInvalidState is returned if the state of the terminal is invalid. + ErrInvalidState = errors.New("Invalid terminal state") +) + +// State represents the state of the terminal. +type State struct { + termios Termios +} + +// RestoreTerminal restores the terminal connected to the given file descriptor +// to a previous state. +func RestoreTerminal(fd uintptr, state *State) error { + if state == nil { + return ErrInvalidState + } + if err := tcset(fd, &state.termios); err != 0 { + return err + } + return nil +} + +// SaveState saves the state of the terminal connected to the given file descriptor. +func SaveState(fd uintptr) (*State, error) { + var oldState State + if err := tcget(fd, &oldState.termios); err != 0 { + return nil, err + } + + return &oldState, nil +} + +// DisableEcho applies the specified state to the terminal connected to the file +// descriptor, with echo disabled. +func DisableEcho(fd uintptr, state *State) error { + newState := state.termios + newState.Lflag &^= unix.ECHO + + if err := tcset(fd, &newState); err != 0 { + return err + } + handleInterrupt(fd, state) + return nil +} + +func handleInterrupt(fd uintptr, state *State) { + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, os.Interrupt) + go func() { + for range sigchan { + // quit cleanly and the new terminal item is on a new line + fmt.Println() + signal.Stop(sigchan) + close(sigchan) + RestoreTerminal(fd, state) + os.Exit(1) + } + }() +} diff --git a/utils/term/termios_bsd.go b/utils/term/termios_bsd.go new file mode 100644 index 0000000..42fd139 --- /dev/null +++ b/utils/term/termios_bsd.go @@ -0,0 +1,15 @@ +// +build darwin freebsd openbsd netbsd + +package term + +import ( + "golang.org/x/sys/unix" +) + +const ( + getTermios = unix.TIOCGETA + setTermios = unix.TIOCSETA +) + +// Termios is the Unix API for terminal I/O. +type Termios unix.Termios diff --git a/utils/term/termios_linux.go b/utils/term/termios_linux.go new file mode 100644 index 0000000..bd7cdb9 --- /dev/null +++ b/utils/term/termios_linux.go @@ -0,0 +1,13 @@ +package term + +import ( + "golang.org/x/sys/unix" +) + +const ( + getTermios = unix.TCGETS + setTermios = unix.TCSETS +) + +// Termios is the Unix API for terminal I/O. +type Termios unix.Termios