|
8 | 8 | "errors" |
9 | 9 | "fmt" |
10 | 10 | "io" |
| 11 | + "math/rand" |
11 | 12 | "net" |
12 | 13 | "os" |
13 | 14 | "os/exec" |
@@ -243,6 +244,15 @@ func logPipeRoutine(r io.Reader, header string) { |
243 | 244 | } |
244 | 245 | } |
245 | 246 |
|
| 247 | +func randomPassword(length int) string { |
| 248 | + passwd := "" |
| 249 | + rand.Seed(time.Now().UnixNano()) |
| 250 | + for i := 0; i < length; i++ { |
| 251 | + passwd += strconv.Itoa(rand.Intn(10)) |
| 252 | + } |
| 253 | + return passwd |
| 254 | +} |
| 255 | + |
246 | 256 | type qArgTemplateApplier struct { |
247 | 257 | files []*os.File |
248 | 258 | } |
@@ -357,6 +367,30 @@ func (a *HostAgent) Run(ctx context.Context) error { |
357 | 367 | qWaitCh <- qCmd.Wait() |
358 | 368 | }() |
359 | 369 |
|
| 370 | + if a.y.Video.VNC.Display != nil { |
| 371 | + vncdisplay := *a.y.Video.VNC.Display |
| 372 | + vnchost, vncnum, err := net.SplitHostPort(vncdisplay) |
| 373 | + if err != nil { |
| 374 | + return err |
| 375 | + } |
| 376 | + n, err := strconv.Atoi(vncnum) |
| 377 | + if err != nil { |
| 378 | + return err |
| 379 | + } |
| 380 | + vncport := strconv.Itoa(5900 + n) |
| 381 | + vncurl := "vnc://" + net.JoinHostPort(vnchost, vncport) |
| 382 | + vncpwdfile := filepath.Join(a.instDir, filenames.VNCPasswordFile) |
| 383 | + vncpasswd := randomPassword(8) |
| 384 | + if err := a.changeVNCPassword(ctx, vncpasswd); err != nil { |
| 385 | + return err |
| 386 | + } |
| 387 | + if err := os.WriteFile(vncpwdfile, []byte(vncpasswd), 0600); err != nil { |
| 388 | + return err |
| 389 | + } |
| 390 | + logrus.Infof("VNC Password for %s <%s> is:", vncdisplay, vncurl) |
| 391 | + logrus.Infof(" \"%s\" | `%s`", vncpasswd, vncpwdfile) |
| 392 | + } |
| 393 | + |
360 | 394 | stBase := events.Status{ |
361 | 395 | SSHLocalPort: a.sshLocalPort, |
362 | 396 | } |
@@ -398,6 +432,46 @@ func (a *HostAgent) Info(ctx context.Context) (*hostagentapi.Info, error) { |
398 | 432 | return info, nil |
399 | 433 | } |
400 | 434 |
|
| 435 | +func waitFileExists(path string, timeout time.Duration) error { |
| 436 | + startWaiting := time.Now() |
| 437 | + for { |
| 438 | + _, err := os.Stat(path) |
| 439 | + if err == nil { |
| 440 | + break |
| 441 | + } |
| 442 | + if !errors.Is(err, os.ErrNotExist) { |
| 443 | + return err |
| 444 | + } |
| 445 | + if time.Since(startWaiting) > timeout { |
| 446 | + return fmt.Errorf("timeout waiting for %s", path) |
| 447 | + } |
| 448 | + time.Sleep(500 * time.Millisecond) |
| 449 | + } |
| 450 | + return nil |
| 451 | +} |
| 452 | + |
| 453 | +func (a *HostAgent) changeVNCPassword(ctx context.Context, password string) error { |
| 454 | + qmpSockPath := filepath.Join(a.instDir, filenames.QMPSock) |
| 455 | + err := waitFileExists(qmpSockPath, 30*time.Second) |
| 456 | + if err != nil { |
| 457 | + return err |
| 458 | + } |
| 459 | + qmpClient, err := qmp.NewSocketMonitor("unix", qmpSockPath, 5*time.Second) |
| 460 | + if err != nil { |
| 461 | + return err |
| 462 | + } |
| 463 | + if err := qmpClient.Connect(); err != nil { |
| 464 | + return err |
| 465 | + } |
| 466 | + defer func() { _ = qmpClient.Disconnect() }() |
| 467 | + rawClient := raw.NewMonitor(qmpClient) |
| 468 | + err = rawClient.ChangeVNCPassword(password) |
| 469 | + if err != nil { |
| 470 | + return err |
| 471 | + } |
| 472 | + return nil |
| 473 | +} |
| 474 | + |
401 | 475 | func (a *HostAgent) shutdownQEMU(ctx context.Context, timeout time.Duration, qCmd *exec.Cmd, qWaitCh <-chan error) error { |
402 | 476 | logrus.Info("Shutting down QEMU with ACPI") |
403 | 477 | qmpSockPath := filepath.Join(a.instDir, filenames.QMPSock) |
|
0 commit comments