I use https://github.com/google/goexpect as my shell interaction, PPPoE dialing program, My operating system CentOS7, my dialing program rp-pppoe, For the first time, such as 1.
How do I write goexpect interactions when I dial?
I know where the problem is and I don't know how to solve it. When I create a dial with concurrency, the logic of the first interaction is different from that of the subsequent one. For example, the content of the second article is different from the content of the first article, and it adds a new question: (default ppp0) or enter 'n' if you Want to create a new one: , I saw that goexpect has an operation of ExpectSwitchCase, but I don't quite understand how to solve my problem with it. How to match one of the interaction logics and ignore other conditions.
The first time the interaction is performed
Welcome to the PPPoE client setup. First, I will run some checks on
your system to make sure the PPPoE client is installed properly...
LOGIN NAME
Enter your Login Name (default root): USER
When I execute the second interaction, How to use google/goexpect to match one in multiple conditions, and continue to perform the next step
Welcome to the PPPoE client setup. First, I will run some checks on
your system to make sure the PPPoE client is installed properly...
The following DSL config was found on your system:
Device: Name:
ppp0 DSLppp0
Please enter the device if you want to configure the present DSL config
(default ppp0) or enter 'n' if you want to create a new one:n
LOGIN NAME
Enter your Login Name: USER
My code, But it can't work properly
package shell
import (
"fmt"
expect "github.com/google/goexpect"
"github.com/google/goterm/term"
"regexp"
"time"
)
func R(user, password, dev string) error {
fmt.Println(term.Bluef("Telnet spawner example"))
to := 3 * time.Second
exec, _, err := expect.Spawn("/usr/sbin/pppoe-setup", to)
if err != nil {
return err
}
defer exec.Close()
r, rs, _, err := exec.ExpectSwitchCase([]expect.Caser{
&expect.Case{R: regexp.MustCompile(`Enter your Login Name \(default root\):`), S: user + "\n", T: expect.Next(), Rt: 0},
&expect.Case{R: regexp.MustCompile(`or enter 'n' if you want to create a new one:`), S: "n\n"},
}, to)
if err != nil {
return err
}
fmt.Println("r:rs:", r, rs)
ur, urs, err := exec.Expect(regexp.MustCompile(`Enter your Login Name`), to)
if err != nil {
return err
}
fmt.Println("ur:urs:", ur, urs)
if err := exec.Send(user + "\n"); err != nil {
fmt.Println("here03:", err.Error())
return err
}
dr, drs, err := exec.Expect(regexp.MustCompile(`\(default eth0\):`), to)
if err != nil {
fmt.Println("here04:", err.Error())
return err
}
fmt.Println(dr, drs)
if err := exec.Send(dev + "\n"); err != nil {
fmt.Println("here05:", err.Error())
return err
}
er, ers, err := exec.Expect(regexp.MustCompile(`Enter the demand value \(default no\):`), to)
if err != nil {
fmt.Println("here06:", err.Error())
return err
}
fmt.Println(er, ers)
if err := exec.Send("\n"); err != nil {
fmt.Println("here06x:", err.Error())
return err
}
if _, _, err := exec.Expect(regexp.MustCompile(`Enter the DNS information here:`), to); err != nil {
fmt.Println("here06x:", err.Error())
return err
}
if err := exec.Send("\n"); err != nil {
fmt.Println("here07:", err.Error())
return err
}
if _, _, err := exec.Expect(regexp.MustCompile(`Please enter your Password:`), to); err != nil {
fmt.Println("here08:", err.Error())
return err
}
if err := exec.Send(password + "\n"); err != nil {
fmt.Println("sendhere08:", err.Error())
return err
}
if _, _, err := exec.Expect(regexp.MustCompile(`Please re-enter your Password:`), to); err != nil {
fmt.Println("here0801:", err.Error())
return err
}
if err := exec.Send(password + "\n"); err != nil {
fmt.Println("sendhere0801:", err.Error())
return err
}
if _, _, err := exec.Expect(regexp.MustCompile(`normal user to start or stop DSL connection \(default yes\):`), to); err != nil {
fmt.Println("here09:", err.Error())
return err
}
if err := exec.Send("yes\n"); err != nil {
fmt.Println("here10:", err.Error())
return err
}
if _, _, err := exec.Expect(regexp.MustCompile(`Choose a type of firewall \(0-2\):`), to); err != nil {
fmt.Println("here11:", err.Error())
return err
}
if err := exec.Send("0\n"); err != nil {
fmt.Println("here12:", err.Error())
return err
}
if _, _, err := exec.Expect(regexp.MustCompile(`Please enter no or yes \(default no\):`), to); err != nil {
fmt.Println("here13:", err.Error())
return err
}
if err := exec.Send("yes\n"); err != nil {
fmt.Println("here14:", err.Error())
return err
}
fr, frs, err := exec.Expect(regexp.MustCompile(`Accept these settings and adjust configuration files \(y/n\)?`), to);
if err != nil {
fmt.Println("here15:", err.Error())
return err
}
fmt.Println(fr,frs)
if err := exec.Send("y\n"); err != nil {
fmt.Println("here16:", err.Error())
return err
}
return nil
}
This is the main file #Josh Chappelle
package main
import (
"fmt"
"github.com/seraphico/ppps/pkg/shell"
)
func main() {
err := shell.R("test01", "test01", "br3")
if err != nil {
fmt.Println(err.Error())
return
}
}
Thanks, I can fix it.
ro, _, _, err := exec.ExpectSwitchCase([]expect.Caser{
&expect.Case{R: regexp.MustCompile("if you want to create a new one:"), S: "n\n", T: expect.Next(), Rt: 1},
&expect.Case{R: regexp.MustCompile(`Enter your Login Name:`), S: user + "\n", Rt: 0},
&expect.Case{R: regexp.MustCompile(`Enter your Login Name \(default root\):`), S: user + "\n", Rt: 0},
}, to)
if err != nil {
return err
}
if if you want to create a new one:If the matching is unsuccessful, the join t status is next and set Rt 1.
Related
I use the ssh package to connect to a Linux server and retrieve the output of commands. The helper function I wrote for that is below:
func sshRunCommand(host string, command string) (output string, err error) {
keyData, err := ioutil.ReadFile("srv.private.openssh")
if err != nil {
return "", err
}
key, err := ssh.ParsePrivateKey(keyData)
if err != nil {
return "", err
}
// Authentication
config := &ssh.ClientConfig{
User: "root",
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Auth: []ssh.AuthMethod{
ssh.PublicKeys(key),
},
}
// Connect
client, err := ssh.Dial("tcp", net.JoinHostPort(host, "22"), config)
if err != nil {
return "", err
}
// Create a session. It is one session per command.
session, err := client.NewSession()
if err != nil {
return "", err
}
defer session.Close()
var b bytes.Buffer
session.Stdout = &b // 👈 this is the place I am concerned with
// Finally, run the command
err = session.Run(command)
return b.String(), err
}
This command usually works fine: it always connects but randomly does not return the output.
Before going further in my investigations, I wanted to make sure that the output buffer is flushed before returning the output. Is this the case?
In this use-case/issue, you can see stdout and stderr being linked to the same buffer.
See if that helps in your case.
sess, err := client.NewSession()
if err != nil {
log.Fatal("Failed to create session: ", err)
}
defer sess.Close()
stdin, err := sess.StdinPipe()
if err != nil {
log.Fatal(err)
}
var b bytes.Buffer
sess.Stdout = &b
sess.Stderr = &b
err = sess.Shell()
if err != nil {
log.Fatal(err)
}
...
_, err = fmt.Fprintf(stdin, "%s\n", cmd)
I'm trying to list directory contents on a share from Linux to Windows using Go.
So far I've managed to Create/Remove new files inside a share with full Read/Write permissions.
Go module: https://godoc.org/github.com/hirochachacha/go-smb2#Client
Functions:
func connect_client(host string, share string, session map[string]string) *smb2.Client {
//Checks for a connection on port
conn, err := net.Dial("tcp", host+":445")
if err != nil {
panic(err)
}
//smb auth
d := &smb2.Dialer{
Initiator: &smb2.NTLMInitiator{
User: session["Username"],
Password: session["Password"],
Domain: session["Domain"],
},
}
//Returns a client session
client, err := d.Dial(conn)
if err != nil {
fmt.Println("Connection failed")
client.Logoff()
} else {
fmt.Println("Connection Succeeded")
}
return client
}
func check_write(host string, client *smb2.Client) {
file := "asdasdas.txt"
fs, err := client.Mount(host)
if err != nil {
fmt.Println(err)
os.Exit(0)
}
defer fs.Umount()
share := strings.Split(host, `\\`)
f, err := fs.Create(file)
if err != nil {
fmt.Println("You do not have write permissions on directory:%s ! \n", strings.Split(share[1], `\`)[1])
os.Exit(0)
}
defer fs.Remove(file)
defer f.Close()
fmt.Printf("You have write permissions to directory: %s \n", strings.Split(share[1], `\`)[1]))
}
func list_all(client *smb2.Client, host string) {
fs, err := client.Mount(host)
if err != nil {
fmt.Println(err)
os.Exit(0)
}
defer fs.Umount()
_, err = fs.Open(`Test.txt`)
if err != nil {
fmt.Println(err)
os.Exit(0)
}
}
func main() {
host, share, action, session := get_flags()
client := connect_client(host, share, session)
full_host := `\\` + host + `\` + share
//File create
if action == "check_write" {
check_write(full_host, client)
}
if action == "list_files" {
list_all(client, full_host)
}
}
In the function list_all() everything works, but when I am trying to access \\192.168.1.19\Sharing only..
When I input just a host with directory name it seas it can not list the directory path because it can not find the object specified.
I can't understand how I can get pointer used for *RemoteFile in order to use the functions:
f.Readdir()
f.Name()
etc....
So far I managed to use *RemoteFileSystem only for all other actions but I want to list all contents of the directory..
Help would be much appreciated!
Edit:
If it wasn't clear enough, in order to use functions like:
f.Readdir()
f.Name()
I need to get a pointer for *RemoteFile, this is my main issue
https://godoc.org/github.com/hirochachacha/go-smb2#RemoteFileSystem.Open
Use Open on a RemoteFileSystem and either a directory name, or empty string for the directory at the root of the filesystem.
e.g.
client, err := d.Dial(conn)
if err != nil {
return err
}
rfs, err := client.Mount("jrwren")
if err != nil {
return err
}
// cat the NOTES file.
f, err := rfs.Open("NOTES")
if err != nil {
return err
}
defer f.Close()
io.Copy(os.Stdout, f)
// List all the files
dir, err := rfs.Open("")
if err != nil {
return err
}
fis, err := dir.Readdir(10)
if err != nil {
return err
}
for i := range fis {
fmt.Println(fis[i].Name())
}
func list_all(client *smb2.Client, host string) {
fs, err := client.Mount(host)
if err != nil {
fmt.Println(err)
os.Exit(0)
}
dir, err := fs.Open("")
if err != nil {
fmt.Println(err)
os.Exit(0)
}
fis, err := dir.Readdir(-1)
if err != nil {
fmt.Println(err)
os.Exit(0)
}
for i := range fis {
fmt.Println(fis[i].Name())
}
}
This would be the answer thank you!
I need to write multicast listener on Go. I faces the problem of twicing packets when I read it. It seems that I need to set IP_MULTICAST_LOOP to false. It is not easy to do in Go.
I found this post. It seems that it should work. But I still get copies of the same host. How should it be done?
ipAddr, err := net.ResolveUDPAddr("udp", groupAddress)
if err != nil {...}
iface, err := net.InterfaceByName("en0")
if err != nil {...}
conn, err := net.ListenPacket("udp4", groupAddress)
if err != nil {...}
pc := ipv4.NewPacketConn(conn)
if err := pc.JoinGroup(iface, ipAddr); err != nil {...}
if err := pc.SetMulticastLoopback(false); err != nil {...}
if loop, err := pc.MulticastLoopback(); err == nil {...}
buf := make([]byte, 1024)
for {
n, _, addr, err := pc.ReadFrom(buf)
if err != nil {...}
fmt.Printf("recv from %v: [%s] \n", addr, buf[:n])
}
The simplest way is to use the ListenMulticastUDP wrapper in the net package, as actually already explained in the other SO answer you point to, How to set IP_MULTICAST_LOOP on multicast UDPConn in Golang.
If you follow the implementation of ListenMulticastUDP(), you will see that at a certain point it calls setIPv4MulticastLoopback(fd, false).
If you need something more advanced, the documentation of ListenMulticastUDP() suggests to look at https://godoc.org/golang.org/x/net/ipv4 and https://godoc.org/golang.org/x/net/ipv6, which document extensively how to do multicast in Go.
Here is some minimal code (tested on MacOS, but platform-independent) that shows how to use ListenMulticastUDP():
func main() {
// MDNS (https://en.wikipedia.org/wiki/Multicast_DNS)
groupAddress := "224.0.0.251:5353"
ifaceName := "en0"
if err := run(groupAddress, ifaceName); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
}
func run(groupAddr string, ifaceName string) error {
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
return err
}
gaddr, err := net.ResolveUDPAddr("udp", groupAddr)
if err != nil {
return err
}
conn, err := net.ListenMulticastUDP("udp", iface, gaddr)
if err != nil {
return err
}
buf := make([]byte, 1024)
for {
n, addr, err := conn.ReadFromUDP(buf)
if err != nil {
return err
}
fmt.Printf("recv %4d bytes from %v\n", n, addr)
}
}
I wonder if possible to copy running .exe file to another folder. I am trying to do this using usual copy approach in Go like that.
func copy(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
return out.Close()
}
...
copyErr := copy(os.Args[0], "D:"+"\\"+"whs.exe")
if copyErr != nil {
log.Panicf("copy -> %v", copyErr)
}
The file copied with the same size but I can't open it correctly. I have only a fast cmd flash. After several milliseconds, cmd is closing and I can't see even any errors.
I was trying to write errors to log file but it's empty.
f, err := os.OpenFile("debug.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0777)
if err != nil {
log.Panicf("setLogOutput -> %v", err)
}
defer f.Close()
log.SetOutput(f)
If I open not copied .exe file everything works correctly.
I've reduced my program to only one main method. The result was the same.
func main() {
log.Println("Starting...")
copyErr := copy(os.Args[0], "F:"+"\\"+"whs.exe")
if copyErr != nil {
log.Panicf("copy -> %v", copyErr)
}
os.Stdin.Read([]byte{0})
}
I have found an error.
The process cannot access the file because it is being used by another process.
I was trying to copy the .exe file to its own path.
func copy(src, dst string) error {
if _, err := os.Stat(dst); os.IsNotExist(err) {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
}
return nil
}
I'm trying to download a remote file over ssh
The following approach works fine on shell
ssh hostname "tar cz /opt/local/folder" > folder.tar.gz
However the same approach on golang giving some difference in output artifact size. For example the same folders with pure shell produce artifact gz file 179B and same with go script 178B.
I assume that something has been missed from io.Reader or session got closed earlier. Kindly ask you guys to help.
Here is the example of my script:
func executeCmd(cmd, hostname string, config *ssh.ClientConfig, path string) error {
conn, _ := ssh.Dial("tcp", hostname+":22", config)
session, err := conn.NewSession()
if err != nil {
panic("Failed to create session: " + err.Error())
}
r, _ := session.StdoutPipe()
scanner := bufio.NewScanner(r)
go func() {
defer session.Close()
name := fmt.Sprintf("%s/backup_folder_%v.tar.gz", path, time.Now().Unix())
file, err := os.OpenFile(name, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
panic(err)
}
defer file.Close()
for scanner.Scan() {
fmt.Println(scanner.Bytes())
if err := scanner.Err(); err != nil {
fmt.Println(err)
}
if _, err = file.Write(scanner.Bytes()); err != nil {
log.Fatal(err)
}
}
}()
if err := session.Run(cmd); err != nil {
fmt.Println(err.Error())
panic("Failed to run: " + err.Error())
}
return nil
}
Thanks!
bufio.Scanner is for newline delimited text. According to the documentation, the scanner will remove the newline characters, stripping any 10s out of your binary file.
You don't need a goroutine to do the copy, because you can use session.Start to start the process asynchronously.
You probably don't need to use bufio either. You should be using io.Copy to copy the file, which has an internal buffer already on top of any buffering already done in the ssh client itself. If an additional buffer is needed for performance, wrap the session output in a bufio.Reader
Finally, you return an error value, so use it rather than panic'ing on regular error conditions.
conn, err := ssh.Dial("tcp", hostname+":22", config)
if err != nil {
return err
}
session, err := conn.NewSession()
if err != nil {
return err
}
defer session.Close()
r, err := session.StdoutPipe()
if err != nil {
return err
}
name := fmt.Sprintf("%s/backup_folder_%v.tar.gz", path, time.Now().Unix())
file, err := os.OpenFile(name, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return err
}
defer file.Close()
if err := session.Start(cmd); err != nil {
return err
}
n, err := io.Copy(file, r)
if err != nil {
return err
}
if err := session.Wait(); err != nil {
return err
}
return nil
You can try doing something like this:
r, _ := session.StdoutPipe()
reader := bufio.NewReader(r)
go func() {
defer session.Close()
// open file etc
// 10 is the number of bytes you'd like to copy in one write operation
p := make([]byte, 10)
for {
n, err := reader.Read(p)
if err == io.EOF {
break
}
if err != nil {
log.Fatal("err", err)
}
if _, err = file.Write(p[:n]); err != nil {
log.Fatal(err)
}
}
}()
Make sure your goroutines are synchronized properly so output is completeky written to the file.