...

Source file src/github.com/coreos/go-systemd/v22/dbus/methods_test.go

Documentation: github.com/coreos/go-systemd/v22/dbus

     1  // Copyright 2015, 2018 CoreOS, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package dbus
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"os"
    21  	"os/exec"
    22  	"path"
    23  	"path/filepath"
    24  	"reflect"
    25  	"syscall"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/godbus/dbus/v5"
    30  )
    31  
    32  type TrUnitProp struct {
    33  	name  string
    34  	props []Property
    35  }
    36  
    37  func setupConn(t *testing.T) *Conn {
    38  	conn, err := New()
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  
    43  	return conn
    44  }
    45  
    46  func findFixture(target string, t *testing.T) string {
    47  	abs, err := filepath.Abs("../fixtures/" + target)
    48  	if err != nil {
    49  		t.Fatal(err)
    50  	}
    51  	return abs
    52  }
    53  
    54  func setupUnit(target string, conn *Conn, t *testing.T) {
    55  	// Blindly stop the unit in case it is running
    56  	conn.StopUnit(target, "replace", nil)
    57  
    58  	// Blindly remove the symlink in case it exists
    59  	targetRun := filepath.Join("/run/systemd/system/", target)
    60  	os.Remove(targetRun)
    61  }
    62  
    63  func linkUnit(target string, conn *Conn, t *testing.T) {
    64  	abs := findFixture(target, t)
    65  	fixture := []string{abs}
    66  
    67  	changes, err := conn.LinkUnitFiles(fixture, true, true)
    68  	if err != nil {
    69  		t.Fatal(err)
    70  	}
    71  
    72  	if len(changes) < 1 {
    73  		t.Fatalf("Expected one change, got %v", changes)
    74  	}
    75  
    76  	runPath := filepath.Join("/run/systemd/system/", target)
    77  	if changes[0].Filename != runPath {
    78  		t.Fatal("Unexpected target filename")
    79  	}
    80  }
    81  
    82  func getUnitStatus(units []UnitStatus, name string) *UnitStatus {
    83  	for _, u := range units {
    84  		if u.Name == name {
    85  			return &u
    86  		}
    87  	}
    88  	return nil
    89  }
    90  
    91  func getUnitStatusSingle(conn *Conn, name string) *UnitStatus {
    92  	units, err := conn.ListUnits()
    93  	if err != nil {
    94  		return nil
    95  	}
    96  	return getUnitStatus(units, name)
    97  }
    98  
    99  func getUnitFile(units []UnitFile, name string) *UnitFile {
   100  	for _, u := range units {
   101  		if path.Base(u.Path) == name {
   102  			return &u
   103  		}
   104  	}
   105  	return nil
   106  }
   107  
   108  func runStartTrUnit(t *testing.T, conn *Conn, trTarget TrUnitProp) error {
   109  	reschan := make(chan string)
   110  	_, err := conn.StartTransientUnit(trTarget.name, "replace", trTarget.props, reschan)
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	job := <-reschan
   116  	if job != "done" {
   117  		return fmt.Errorf("Job is not done: %s", job)
   118  	}
   119  
   120  	return nil
   121  }
   122  
   123  func runStopUnit(t *testing.T, conn *Conn, trTarget TrUnitProp) error {
   124  	reschan := make(chan string)
   125  	_, err := conn.StopUnit(trTarget.name, "replace", reschan)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	// wait for StopUnit job to complete
   131  	<-reschan
   132  
   133  	return nil
   134  }
   135  
   136  func getJobStatusIfExists(jobs []JobStatus, jobName string) *JobStatus {
   137  	for _, j := range jobs {
   138  		if j.Unit == jobName {
   139  			return &j
   140  		}
   141  	}
   142  	return nil
   143  }
   144  
   145  func isJobStatusEmpty(job JobStatus) bool {
   146  	return job.Id == 0 && job.Unit == "" && job.JobType == "" && job.Status == "" && job.JobPath == "" && job.UnitPath == ""
   147  }
   148  
   149  // Ensure that basic unit starting and stopping works.
   150  func TestStartStopUnit(t *testing.T) {
   151  	target := "start-stop.service"
   152  	conn := setupConn(t)
   153  	defer conn.Close()
   154  
   155  	setupUnit(target, conn, t)
   156  	linkUnit(target, conn, t)
   157  
   158  	// 2. Start the unit
   159  	reschan := make(chan string)
   160  	_, err := conn.StartUnit(target, "replace", reschan)
   161  	if err != nil {
   162  		t.Fatal(err)
   163  	}
   164  
   165  	job := <-reschan
   166  	if job != "done" {
   167  		t.Fatal("Job is not done:", job)
   168  	}
   169  
   170  	units, err := conn.ListUnits()
   171  	if err != nil {
   172  		t.Fatal(err)
   173  	}
   174  
   175  	unit := getUnitStatus(units, target)
   176  
   177  	if unit == nil {
   178  		t.Fatalf("Test unit not found in list")
   179  	} else if unit.ActiveState != "active" {
   180  		t.Fatalf("Test unit not active")
   181  	}
   182  
   183  	// 3. Stop the unit
   184  	_, err = conn.StopUnit(target, "replace", reschan)
   185  	if err != nil {
   186  		t.Fatal(err)
   187  	}
   188  
   189  	// wait for StopUnit job to complete
   190  	<-reschan
   191  
   192  	units, err = conn.ListUnits()
   193  	if err != nil {
   194  		t.Fatal(err)
   195  	}
   196  
   197  	unit = getUnitStatus(units, target)
   198  
   199  	if unit != nil {
   200  		t.Fatalf("Test unit found in list, should be stopped")
   201  	}
   202  }
   203  
   204  // Ensure that basic unit restarting works.
   205  func TestRestartUnit(t *testing.T) {
   206  	target := "start-stop.service"
   207  	conn := setupConn(t)
   208  	defer conn.Close()
   209  
   210  	setupUnit(target, conn, t)
   211  	linkUnit(target, conn, t)
   212  
   213  	// Start the unit
   214  	reschan := make(chan string)
   215  	_, err := conn.StartUnit(target, "replace", reschan)
   216  	if err != nil {
   217  		t.Fatal(err)
   218  	}
   219  
   220  	job := <-reschan
   221  	if job != "done" {
   222  		t.Fatal("Job is not done:", job)
   223  	}
   224  
   225  	units, err := conn.ListUnits()
   226  	if err != nil {
   227  		t.Fatal(err)
   228  	}
   229  
   230  	unit := getUnitStatus(units, target)
   231  	if unit == nil {
   232  		t.Fatalf("Test unit not found in list")
   233  	} else if unit.ActiveState != "active" {
   234  		t.Fatalf("Test unit not active")
   235  	}
   236  
   237  	// Restart the unit
   238  	reschan = make(chan string)
   239  	_, err = conn.RestartUnit(target, "replace", reschan)
   240  	if err != nil {
   241  		t.Fatal(err)
   242  	}
   243  
   244  	job = <-reschan
   245  	if job != "done" {
   246  		t.Fatal("Job is not done:", job)
   247  	}
   248  
   249  	// Stop the unit
   250  	_, err = conn.StopUnit(target, "replace", reschan)
   251  	if err != nil {
   252  		t.Fatal(err)
   253  	}
   254  
   255  	// wait for StopUnit job to complete
   256  	<-reschan
   257  
   258  	units, err = conn.ListUnits()
   259  	if err != nil {
   260  		t.Fatal(err)
   261  	}
   262  
   263  	unit = getUnitStatus(units, target)
   264  	if unit != nil {
   265  		t.Fatalf("Test unit found in list, should be stopped")
   266  	}
   267  
   268  	// Try to restart the unit.
   269  	// It should still succeed, even if the unit is inactive.
   270  	reschan = make(chan string)
   271  	_, err = conn.TryRestartUnit(target, "replace", reschan)
   272  	if err != nil {
   273  		t.Fatal(err)
   274  	}
   275  
   276  	// wait for StopUnit job to complete
   277  	<-reschan
   278  
   279  	units, err = conn.ListUnits()
   280  	if err != nil {
   281  		t.Fatal(err)
   282  	}
   283  
   284  	unit = getUnitStatus(units, target)
   285  	if unit != nil {
   286  		t.Fatalf("Test unit found in list, should be stopped")
   287  	}
   288  }
   289  
   290  // Ensure that basic unit reloading works.
   291  func TestReloadUnit(t *testing.T) {
   292  	target := "reload.service"
   293  	conn := setupConn(t)
   294  	defer conn.Close()
   295  
   296  	err := conn.Subscribe()
   297  	if err != nil {
   298  		t.Fatal(err)
   299  	}
   300  
   301  	subSet := conn.NewSubscriptionSet()
   302  	evChan, errChan := subSet.Subscribe()
   303  
   304  	subSet.Add(target)
   305  
   306  	setupUnit(target, conn, t)
   307  	linkUnit(target, conn, t)
   308  
   309  	// Start the unit
   310  	reschan := make(chan string)
   311  	_, err = conn.StartUnit(target, "replace", reschan)
   312  	if err != nil {
   313  		t.Fatal(err)
   314  	}
   315  
   316  	job := <-reschan
   317  	if job != "done" {
   318  		t.Fatal("Job is not done:", job)
   319  	}
   320  
   321  	units, err := conn.ListUnits()
   322  	if err != nil {
   323  		t.Fatal(err)
   324  	}
   325  
   326  	unit := getUnitStatus(units, target)
   327  	if unit == nil {
   328  		t.Fatalf("Test unit not found in list")
   329  	} else if unit.ActiveState != "active" {
   330  		t.Fatalf("Test unit not active")
   331  	}
   332  
   333  	// Reload the unit
   334  	reschan = make(chan string)
   335  
   336  	_, err = conn.ReloadUnit(target, "replace", reschan)
   337  	if err != nil {
   338  		t.Fatal(err)
   339  	}
   340  
   341  	job = <-reschan
   342  	if job != "done" {
   343  		t.Fatal("Job is not done:", job)
   344  	}
   345  
   346  	timeout := make(chan bool, 1)
   347  	go func() {
   348  		time.Sleep(3 * time.Second)
   349  		close(timeout)
   350  	}()
   351  
   352  	// Wait for the event, expecting the target UnitStatus meets all of the
   353  	// following conditions:
   354  	//  * target is non-nil
   355  	//  * target's ActiveState is active.
   356  waitevent:
   357  	for {
   358  		select {
   359  		case changes := <-evChan:
   360  			tch, ok := changes[target]
   361  			if !ok {
   362  				continue waitevent
   363  			}
   364  			if tch != nil && tch.Name == target && tch.ActiveState == "active" {
   365  				break waitevent
   366  			}
   367  		case err = <-errChan:
   368  			t.Fatal(err)
   369  		case <-timeout:
   370  			t.Fatal("Reached timeout")
   371  		}
   372  	}
   373  }
   374  
   375  // Ensure that basic unit reload-or-restarting works.
   376  func TestReloadOrRestartUnit(t *testing.T) {
   377  	target := "reload.service"
   378  	conn := setupConn(t)
   379  	defer conn.Close()
   380  
   381  	setupUnit(target, conn, t)
   382  	linkUnit(target, conn, t)
   383  
   384  	// Start the unit
   385  	reschan := make(chan string)
   386  	_, err := conn.StartUnit(target, "replace", reschan)
   387  	if err != nil {
   388  		t.Fatal(err)
   389  	}
   390  
   391  	job := <-reschan
   392  	if job != "done" {
   393  		t.Fatal("Job is not done:", job)
   394  	}
   395  
   396  	units, err := conn.ListUnits()
   397  	if err != nil {
   398  		t.Fatal(err)
   399  	}
   400  
   401  	unit := getUnitStatus(units, target)
   402  	if unit == nil {
   403  		t.Fatalf("Test unit not found in list")
   404  	} else if unit.ActiveState != "active" {
   405  		t.Fatalf("Test unit not active")
   406  	}
   407  
   408  	// Reload or restart the unit
   409  	reschan = make(chan string)
   410  	_, err = conn.ReloadOrRestartUnit(target, "replace", reschan)
   411  	if err != nil {
   412  		t.Fatal(err)
   413  	}
   414  
   415  	job = <-reschan
   416  	if job != "done" {
   417  		t.Fatal("Job is not done:", job)
   418  	}
   419  
   420  	// Stop the unit
   421  	_, err = conn.StopUnit(target, "replace", reschan)
   422  	if err != nil {
   423  		t.Fatal(err)
   424  	}
   425  
   426  	// wait for StopUnit job to complete
   427  	<-reschan
   428  
   429  	units, err = conn.ListUnits()
   430  	if err != nil {
   431  		t.Fatal(err)
   432  	}
   433  
   434  	unit = getUnitStatus(units, target)
   435  	if unit != nil && unit.ActiveState == "active" {
   436  		t.Fatalf("Test unit still active, should be inactive.")
   437  	}
   438  
   439  	// Reload or try to restart the unit
   440  	// It should still succeed, even if the unit is inactive.
   441  	reschan = make(chan string)
   442  	_, err = conn.ReloadOrTryRestartUnit(target, "replace", reschan)
   443  	if err != nil {
   444  		t.Fatal(err)
   445  	}
   446  
   447  	job = <-reschan
   448  	if job != "done" {
   449  		t.Fatal("Job is not done:", job)
   450  	}
   451  }
   452  
   453  // Ensure that GetUnitByPID works.
   454  func TestGetUnitByPID(t *testing.T) {
   455  	conn := setupConn(t)
   456  	defer conn.Close()
   457  
   458  	path, err := conn.GetUnitByPID(context.Background(), 1)
   459  
   460  	if err != nil {
   461  		t.Error(err)
   462  	}
   463  
   464  	if path == "" {
   465  		t.Fatal("path is empty")
   466  	}
   467  }
   468  
   469  // Ensure that GetUnitNameByPID works.
   470  func TestGetUnitNameByPID(t *testing.T) {
   471  	conn := setupConn(t)
   472  	defer conn.Close()
   473  
   474  	name, err := conn.GetUnitNameByPID(context.Background(), 1)
   475  
   476  	if err != nil {
   477  		t.Error(err)
   478  	}
   479  
   480  	if name == "" {
   481  		t.Fatal("name is empty")
   482  	}
   483  }
   484  
   485  // Ensure that ListUnitsByNames works.
   486  func TestListUnitsByNames(t *testing.T) {
   487  	target1 := "systemd-journald.service"
   488  	target2 := "unexisting.service"
   489  
   490  	conn := setupConn(t)
   491  	defer conn.Close()
   492  
   493  	units, err := conn.ListUnitsByNames([]string{target1, target2})
   494  
   495  	if err != nil {
   496  		t.Skip(err)
   497  	}
   498  
   499  	unit := getUnitStatus(units, target1)
   500  
   501  	if unit == nil {
   502  		t.Fatalf("%s unit not found in list", target1)
   503  	} else if unit.ActiveState != "active" {
   504  		t.Fatalf("%s unit should be active but it is %s", target1, unit.ActiveState)
   505  	}
   506  
   507  	unit = getUnitStatus(units, target2)
   508  
   509  	if unit == nil {
   510  		t.Fatalf("Unexisting test unit not found in list")
   511  	} else if unit.ActiveState != "inactive" {
   512  		t.Fatalf("Test unit should be inactive")
   513  	}
   514  }
   515  
   516  // Ensure that ListUnitsByPatterns works.
   517  func TestListUnitsByPatterns(t *testing.T) {
   518  	target1 := "systemd-journald.service"
   519  	target2 := "unexisting.service"
   520  
   521  	conn := setupConn(t)
   522  	defer conn.Close()
   523  
   524  	units, err := conn.ListUnitsByPatterns([]string{}, []string{"systemd-journald*", target2})
   525  
   526  	if err != nil {
   527  		t.Skip(err)
   528  	}
   529  
   530  	unit := getUnitStatus(units, target1)
   531  
   532  	if unit == nil {
   533  		t.Fatalf("%s unit not found in list", target1)
   534  	} else if unit.ActiveState != "active" {
   535  		t.Fatalf("Test unit should be active")
   536  	}
   537  
   538  	unit = getUnitStatus(units, target2)
   539  
   540  	if unit != nil {
   541  		t.Fatalf("Unexisting test unit found in list")
   542  	}
   543  }
   544  
   545  // Ensure that ListUnitsFiltered works.
   546  func TestListUnitsFiltered(t *testing.T) {
   547  	target := "systemd-journald.service"
   548  
   549  	conn := setupConn(t)
   550  	defer conn.Close()
   551  
   552  	units, err := conn.ListUnitsFiltered([]string{"active"})
   553  
   554  	if err != nil {
   555  		t.Fatal(err)
   556  	}
   557  
   558  	unit := getUnitStatus(units, target)
   559  
   560  	if unit == nil {
   561  		t.Fatalf("%s unit not found in list", target)
   562  	} else if unit.ActiveState != "active" {
   563  		t.Fatalf("Test unit should be active")
   564  	}
   565  
   566  	units, err = conn.ListUnitsFiltered([]string{"inactive"})
   567  
   568  	if err != nil {
   569  		t.Fatal(err)
   570  	}
   571  
   572  	unit = getUnitStatus(units, target)
   573  
   574  	if unit != nil {
   575  		t.Fatalf("Inactive unit should not be found in list")
   576  	}
   577  }
   578  
   579  // Ensure that ListUnitFilesByPatterns works.
   580  func TestListUnitFilesByPatterns(t *testing.T) {
   581  	target1 := "systemd-journald.service"
   582  	target2 := "exit.target"
   583  
   584  	conn := setupConn(t)
   585  	defer conn.Close()
   586  
   587  	units, err := conn.ListUnitFilesByPatterns([]string{"static"}, []string{"systemd-journald*", target2})
   588  
   589  	if err != nil {
   590  		t.Skip(err)
   591  	}
   592  
   593  	unit := getUnitFile(units, target1)
   594  
   595  	if unit == nil {
   596  		t.Fatalf("%s unit not found in list", target1)
   597  	} else if unit.Type != "static" {
   598  		t.Fatalf("Test unit file should be static")
   599  	}
   600  
   601  	units, err = conn.ListUnitFilesByPatterns([]string{"disabled"}, []string{"systemd-journald*", target2})
   602  
   603  	if err != nil {
   604  		t.Fatal(err)
   605  	}
   606  
   607  	unit = getUnitFile(units, target2)
   608  
   609  	if unit == nil {
   610  		t.Fatalf("%s unit not found in list", target2)
   611  	} else if unit.Type != "disabled" {
   612  		t.Fatalf("%s unit file should be disabled", target2)
   613  	}
   614  }
   615  
   616  func TestListUnitFiles(t *testing.T) {
   617  	target1 := "systemd-journald.service"
   618  	target2 := "exit.target"
   619  
   620  	conn := setupConn(t)
   621  	defer conn.Close()
   622  
   623  	units, err := conn.ListUnitFiles()
   624  
   625  	if err != nil {
   626  		t.Fatal(err)
   627  	}
   628  
   629  	unit := getUnitFile(units, target1)
   630  
   631  	if unit == nil {
   632  		t.Fatalf("%s unit not found in list", target1)
   633  	} else if unit.Type != "static" {
   634  		t.Fatalf("Test unit file should be static")
   635  	}
   636  
   637  	unit = getUnitFile(units, target2)
   638  
   639  	if unit == nil {
   640  		t.Fatalf("%s unit not found in list", target2)
   641  	} else if unit.Type != "disabled" {
   642  		t.Fatalf("%s unit file should be disabled", target2)
   643  	}
   644  }
   645  
   646  // Enables a unit and then immediately tears it down
   647  func TestEnableDisableUnit(t *testing.T) {
   648  	target := "enable-disable.service"
   649  	conn := setupConn(t)
   650  	defer conn.Close()
   651  
   652  	setupUnit(target, conn, t)
   653  	abs := findFixture(target, t)
   654  	runPath := filepath.Join("/run/systemd/system/", target)
   655  
   656  	// 1. Enable the unit
   657  	install, changes, err := conn.EnableUnitFiles([]string{abs}, true, true)
   658  	if err != nil {
   659  		t.Fatal(err)
   660  	}
   661  
   662  	if install {
   663  		t.Log("Install was true")
   664  	}
   665  
   666  	if len(changes) < 1 {
   667  		t.Fatalf("Expected one change, got %v", changes)
   668  	}
   669  
   670  	if changes[0].Filename != runPath {
   671  		t.Fatal("Unexpected target filename")
   672  	}
   673  
   674  	// 2. Disable the unit
   675  	dChanges, err := conn.DisableUnitFiles([]string{target}, true)
   676  	if err != nil {
   677  		t.Fatal(err)
   678  	}
   679  
   680  	if len(dChanges) != 1 {
   681  		t.Fatalf("Changes should include the path, %v", dChanges)
   682  	}
   683  	if dChanges[0].Filename != runPath {
   684  		t.Fatalf("Change should include correct filename, %+v", dChanges[0])
   685  	}
   686  	if dChanges[0].Destination != "" {
   687  		t.Fatalf("Change destination should be empty, %+v", dChanges[0])
   688  	}
   689  }
   690  
   691  // Ensure that ListJobs works.
   692  func TestListJobs(t *testing.T) {
   693  	service := "oneshot.service"
   694  
   695  	conn := setupConn(t)
   696  
   697  	setupUnit(service, conn, t)
   698  	linkUnit(service, conn, t)
   699  
   700  	_, err := conn.StartUnit(service, "replace", nil)
   701  	if err != nil {
   702  		t.Fatal(err)
   703  	}
   704  
   705  	jobs, err := conn.ListJobs()
   706  	if err != nil {
   707  		t.Skip(err)
   708  	}
   709  
   710  	found := getJobStatusIfExists(jobs, service)
   711  	if found == nil {
   712  		t.Fatalf("%s job not found in list", service)
   713  	}
   714  
   715  	if isJobStatusEmpty(*found) {
   716  		t.Fatalf("empty %s job found in list", service)
   717  	}
   718  
   719  	reschan := make(chan string)
   720  	_, err = conn.StopUnit(service, "replace", reschan)
   721  	if err != nil {
   722  		t.Fatal(err)
   723  	}
   724  
   725  	<-reschan
   726  
   727  	jobs, err = conn.ListJobs()
   728  
   729  	found = getJobStatusIfExists(jobs, service)
   730  	if err != nil {
   731  		t.Fatal(err)
   732  	}
   733  
   734  	if found != nil {
   735  		t.Fatalf("%s job found in list when it shouldn't", service)
   736  	}
   737  }
   738  
   739  // TestSystemState tests if system state is one of the valid states
   740  func TestSystemState(t *testing.T) {
   741  	conn := setupConn(t)
   742  	defer conn.Close()
   743  
   744  	prop, err := conn.SystemState()
   745  	if err != nil {
   746  		t.Fatal(err)
   747  	}
   748  
   749  	if prop.Name != "SystemState" {
   750  		t.Fatalf("unexpected property name: %v", prop.Name)
   751  	}
   752  
   753  	val := prop.Value.Value().(string)
   754  
   755  	switch val {
   756  	case "initializing":
   757  	case "starting":
   758  	case "running":
   759  	case "degraded":
   760  	case "maintenance":
   761  	case "stopping":
   762  	case "offline":
   763  	case "unknown":
   764  		// valid systemd state - do nothing
   765  
   766  	default:
   767  		t.Fatalf("unexpected property value: %v", val)
   768  	}
   769  }
   770  
   771  // TestGetUnitProperties reads the `-.mount` which should exist on all systemd
   772  // systems and ensures that one of its properties is valid.
   773  func TestGetUnitProperties(t *testing.T) {
   774  	conn := setupConn(t)
   775  	defer conn.Close()
   776  
   777  	unit := "-.mount"
   778  
   779  	info, err := conn.GetUnitProperties(unit)
   780  	if err != nil {
   781  		t.Fatal(err)
   782  	}
   783  
   784  	desc, _ := info["Description"].(string)
   785  
   786  	prop, err := conn.GetUnitProperty(unit, "Description")
   787  	if err != nil {
   788  		t.Fatal(err)
   789  	}
   790  
   791  	if prop.Name != "Description" {
   792  		t.Fatal("unexpected property name")
   793  	}
   794  
   795  	val := prop.Value.Value().(string)
   796  	if !reflect.DeepEqual(val, desc) {
   797  		t.Fatal("unexpected property value")
   798  	}
   799  }
   800  
   801  // TestGetUnitPropertiesRejectsInvalidName attempts to get the properties for a
   802  // unit with an invalid name. This test should be run with --test.timeout set,
   803  // as a fail will manifest as GetUnitProperties hanging indefinitely.
   804  func TestGetUnitPropertiesRejectsInvalidName(t *testing.T) {
   805  	conn := setupConn(t)
   806  	defer conn.Close()
   807  
   808  	unit := "//invalid#$^/"
   809  
   810  	_, err := conn.GetUnitProperties(unit)
   811  	if err == nil {
   812  		t.Fatal("Expected an error, got nil")
   813  	}
   814  
   815  	_, err = conn.GetUnitProperty(unit, "Wants")
   816  	if err == nil {
   817  		t.Fatal("Expected an error, got nil")
   818  	}
   819  }
   820  
   821  // TestGetServiceProperty reads the `systemd-udevd.service` which should exist
   822  // on all systemd systems and ensures that one of its property is valid.
   823  func TestGetServiceProperty(t *testing.T) {
   824  	conn := setupConn(t)
   825  	defer conn.Close()
   826  
   827  	service := "systemd-udevd.service"
   828  
   829  	prop, err := conn.GetServiceProperty(service, "Type")
   830  	if err != nil {
   831  		t.Fatal(err)
   832  	}
   833  
   834  	if prop.Name != "Type" {
   835  		t.Fatal("unexpected property name")
   836  	}
   837  
   838  	if _, ok := prop.Value.Value().(string); !ok {
   839  		t.Fatal("invalid property value")
   840  	}
   841  }
   842  
   843  // TestSetUnitProperties changes a cgroup setting on the `-.mount`
   844  // which should exist on all systemd systems and ensures that the
   845  // property was set.
   846  func TestSetUnitProperties(t *testing.T) {
   847  	conn := setupConn(t)
   848  	defer conn.Close()
   849  
   850  	unit := "-.mount"
   851  
   852  	if err := conn.SetUnitProperties(unit, true, Property{"CPUShares", dbus.MakeVariant(uint64(1023))}); err != nil {
   853  		t.Fatal(err)
   854  	}
   855  
   856  	info, err := conn.GetUnitTypeProperties(unit, "Mount")
   857  	if err != nil {
   858  		t.Fatal(err)
   859  	}
   860  
   861  	value, _ := info["CPUShares"].(uint64)
   862  	if value != 1023 {
   863  		t.Fatal("CPUShares of unit is not 1023:", value)
   864  	}
   865  }
   866  
   867  // Ensure that oneshot transient unit starting and stopping works.
   868  func TestStartStopTransientUnitAll(t *testing.T) {
   869  	testCases := []struct {
   870  		trTarget  TrUnitProp
   871  		trDep     TrUnitProp
   872  		checkFunc func(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error
   873  	}{
   874  		{
   875  			trTarget: TrUnitProp{
   876  				name: "testing-transient.service",
   877  				props: []Property{
   878  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   879  				},
   880  			},
   881  			trDep:     TrUnitProp{"", nil},
   882  			checkFunc: checkTransientUnit,
   883  		},
   884  		{
   885  			trTarget: TrUnitProp{
   886  				name: "testing-transient-oneshot.service",
   887  				props: []Property{
   888  					PropExecStart([]string{"/bin/true"}, false),
   889  					PropType("oneshot"),
   890  					PropRemainAfterExit(true),
   891  				},
   892  			},
   893  			trDep:     TrUnitProp{"", nil},
   894  			checkFunc: checkTransientUnitOneshot,
   895  		},
   896  		{
   897  			trTarget: TrUnitProp{
   898  				name: "testing-transient-requires.service",
   899  				props: []Property{
   900  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   901  					PropRequires("testing-transient-requiresdep.service"),
   902  				},
   903  			},
   904  			trDep: TrUnitProp{
   905  				name: "testing-transient-requiresdep.service",
   906  				props: []Property{
   907  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   908  				},
   909  			},
   910  			checkFunc: checkTransientUnitRequires,
   911  		},
   912  		{
   913  			trTarget: TrUnitProp{
   914  				name: "testing-transient-requires-ov.service",
   915  				props: []Property{
   916  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   917  					PropRequires("testing-transient-requiresdep-ov.service"),
   918  				},
   919  			},
   920  			trDep: TrUnitProp{
   921  				name: "testing-transient-requiresdep-ov.service",
   922  				props: []Property{
   923  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   924  				},
   925  			},
   926  			checkFunc: checkTransientUnitRequiresOv,
   927  		},
   928  		{
   929  			trTarget: TrUnitProp{
   930  				name: "testing-transient-requisite.service",
   931  				props: []Property{
   932  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   933  					PropRequisite("testing-transient-requisitedep.service"),
   934  				},
   935  			},
   936  			trDep: TrUnitProp{
   937  				name: "testing-transient-requisitedep.service",
   938  				props: []Property{
   939  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   940  				},
   941  			},
   942  			checkFunc: checkTransientUnitRequisite,
   943  		},
   944  		{
   945  			trTarget: TrUnitProp{
   946  				name: "testing-transient-requisite-ov.service",
   947  				props: []Property{
   948  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   949  					PropRequisiteOverridable("testing-transient-requisitedep-ov.service"),
   950  				},
   951  			},
   952  			trDep: TrUnitProp{
   953  				name: "testing-transient-requisitedep-ov.service",
   954  				props: []Property{
   955  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   956  				},
   957  			},
   958  			checkFunc: checkTransientUnitRequisiteOv,
   959  		},
   960  		{
   961  			trTarget: TrUnitProp{
   962  				name: "testing-transient-wants.service",
   963  				props: []Property{
   964  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   965  					PropWants("testing-transient-wantsdep.service"),
   966  				},
   967  			},
   968  			trDep: TrUnitProp{
   969  				name: "testing-transient-wantsdep.service",
   970  				props: []Property{
   971  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   972  				},
   973  			},
   974  			checkFunc: checkTransientUnitWants,
   975  		},
   976  		{
   977  			trTarget: TrUnitProp{
   978  				name: "testing-transient-bindsto.service",
   979  				props: []Property{
   980  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   981  					PropBindsTo("testing-transient-bindstodep.service"),
   982  				},
   983  			},
   984  			trDep: TrUnitProp{
   985  				name: "testing-transient-bindstodep.service",
   986  				props: []Property{
   987  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   988  				},
   989  			},
   990  			checkFunc: checkTransientUnitBindsTo,
   991  		},
   992  		{
   993  			trTarget: TrUnitProp{
   994  				name: "testing-transient-conflicts.service",
   995  				props: []Property{
   996  					PropExecStart([]string{"/bin/sleep", "400"}, false),
   997  					PropConflicts("testing-transient-conflictsdep.service"),
   998  				},
   999  			},
  1000  			trDep: TrUnitProp{
  1001  				name: "testing-transient-conflictsdep.service",
  1002  				props: []Property{
  1003  					PropExecStart([]string{"/bin/sleep", "400"}, false),
  1004  				},
  1005  			},
  1006  			checkFunc: checkTransientUnitConflicts,
  1007  		},
  1008  	}
  1009  
  1010  	for i, tt := range testCases {
  1011  		if err := tt.checkFunc(t, tt.trTarget, tt.trDep); err != nil {
  1012  			t.Errorf("case %d: failed test with unit %s. err: %v", i, tt.trTarget.name, err)
  1013  		}
  1014  	}
  1015  }
  1016  
  1017  // Ensure that basic transient unit starting and stopping works.
  1018  func checkTransientUnit(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
  1019  	conn := setupConn(t)
  1020  	defer conn.Close()
  1021  
  1022  	// Start the unit
  1023  	err := runStartTrUnit(t, conn, trTarget)
  1024  	if err != nil {
  1025  		return err
  1026  	}
  1027  
  1028  	unit := getUnitStatusSingle(conn, trTarget.name)
  1029  	if unit == nil {
  1030  		return fmt.Errorf("Test unit not found in list")
  1031  	} else if unit.ActiveState != "active" {
  1032  		return fmt.Errorf("Test unit not active")
  1033  	}
  1034  
  1035  	// Stop the unit
  1036  	err = runStopUnit(t, conn, trTarget)
  1037  	if err != nil {
  1038  		return err
  1039  	}
  1040  
  1041  	unit = getUnitStatusSingle(conn, trTarget.name)
  1042  	if unit != nil {
  1043  		return fmt.Errorf("Test unit found in list, should be stopped")
  1044  	}
  1045  
  1046  	return nil
  1047  }
  1048  
  1049  func checkTransientUnitOneshot(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
  1050  	conn := setupConn(t)
  1051  	defer conn.Close()
  1052  
  1053  	// Start the unit
  1054  	err := runStartTrUnit(t, conn, trTarget)
  1055  	if err != nil {
  1056  		return err
  1057  	}
  1058  
  1059  	unit := getUnitStatusSingle(conn, trTarget.name)
  1060  	if unit == nil {
  1061  		return fmt.Errorf("Test unit not found in list")
  1062  	} else if unit.ActiveState != "active" {
  1063  		return fmt.Errorf("Test unit not active")
  1064  	}
  1065  
  1066  	// Stop the unit
  1067  	err = runStopUnit(t, conn, trTarget)
  1068  	if err != nil {
  1069  		return err
  1070  	}
  1071  
  1072  	unit = getUnitStatusSingle(conn, trTarget.name)
  1073  	if unit != nil {
  1074  		return fmt.Errorf("Test unit found in list, should be stopped")
  1075  	}
  1076  
  1077  	return nil
  1078  }
  1079  
  1080  // Ensure that transient unit with Requires starting and stopping works.
  1081  func checkTransientUnitRequires(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
  1082  	conn := setupConn(t)
  1083  	defer conn.Close()
  1084  
  1085  	// Start the dependent unit
  1086  	err := runStartTrUnit(t, conn, trDep)
  1087  	if err != nil {
  1088  		return err
  1089  	}
  1090  
  1091  	// Start the target unit
  1092  	err = runStartTrUnit(t, conn, trTarget)
  1093  	if err != nil {
  1094  		return err
  1095  	}
  1096  
  1097  	unit := getUnitStatusSingle(conn, trTarget.name)
  1098  	if unit == nil {
  1099  		return fmt.Errorf("Test unit not found in list")
  1100  	} else if unit.ActiveState != "active" {
  1101  		return fmt.Errorf("Test unit not active")
  1102  	}
  1103  
  1104  	// Stop the unit
  1105  	err = runStopUnit(t, conn, trTarget)
  1106  	if err != nil {
  1107  		return err
  1108  	}
  1109  
  1110  	unit = getUnitStatusSingle(conn, trTarget.name)
  1111  	if unit != nil {
  1112  		return fmt.Errorf("Test unit found in list, should be stopped")
  1113  	}
  1114  
  1115  	// Stop the dependent unit
  1116  	err = runStopUnit(t, conn, trDep)
  1117  	if err != nil {
  1118  		return err
  1119  	}
  1120  
  1121  	unit = getUnitStatusSingle(conn, trDep.name)
  1122  	if unit != nil {
  1123  		return fmt.Errorf("Test unit found in list, should be stopped")
  1124  	}
  1125  
  1126  	return nil
  1127  }
  1128  
  1129  // Ensure that transient unit with RequiresOverridable starting and stopping works.
  1130  func checkTransientUnitRequiresOv(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
  1131  	conn := setupConn(t)
  1132  	defer conn.Close()
  1133  
  1134  	// Start the dependent unit
  1135  	err := runStartTrUnit(t, conn, trDep)
  1136  	if err != nil {
  1137  		return err
  1138  	}
  1139  
  1140  	// Start the target unit
  1141  	err = runStartTrUnit(t, conn, trTarget)
  1142  	if err != nil {
  1143  		return err
  1144  	}
  1145  
  1146  	unit := getUnitStatusSingle(conn, trTarget.name)
  1147  	if unit == nil {
  1148  		return fmt.Errorf("Test unit not found in list")
  1149  	} else if unit.ActiveState != "active" {
  1150  		return fmt.Errorf("Test unit not active")
  1151  	}
  1152  
  1153  	// Stop the unit
  1154  	err = runStopUnit(t, conn, trTarget)
  1155  	if err != nil {
  1156  		return err
  1157  	}
  1158  
  1159  	unit = getUnitStatusSingle(conn, trTarget.name)
  1160  	if unit != nil {
  1161  		return fmt.Errorf("Test unit found in list, should be stopped")
  1162  	}
  1163  
  1164  	// Stop the dependent unit
  1165  	err = runStopUnit(t, conn, trDep)
  1166  	if err != nil {
  1167  		return err
  1168  	}
  1169  
  1170  	unit = getUnitStatusSingle(conn, trDep.name)
  1171  	if unit != nil {
  1172  		return fmt.Errorf("Test unit found in list, should be stopped")
  1173  	}
  1174  
  1175  	return nil
  1176  }
  1177  
  1178  // Ensure that transient unit with Requisite starting and stopping works.
  1179  // It's expected for target unit to fail, as its child is not started at all.
  1180  func checkTransientUnitRequisite(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
  1181  	conn := setupConn(t)
  1182  	defer conn.Close()
  1183  
  1184  	// Start the target unit
  1185  	err := runStartTrUnit(t, conn, trTarget)
  1186  	if err == nil {
  1187  		return fmt.Errorf("Unit %s is expected to fail, but succeeded", trTarget.name)
  1188  	}
  1189  
  1190  	unit := getUnitStatusSingle(conn, trTarget.name)
  1191  	if unit != nil && unit.ActiveState == "active" {
  1192  		return fmt.Errorf("Test unit %s is active, should be inactive", trTarget.name)
  1193  	}
  1194  
  1195  	return nil
  1196  }
  1197  
  1198  // Ensure that transient unit with RequisiteOverridable starting and stopping works.
  1199  // It's expected for target unit to fail, as its child is not started at all.
  1200  func checkTransientUnitRequisiteOv(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
  1201  	conn := setupConn(t)
  1202  	defer conn.Close()
  1203  
  1204  	// Start the target unit
  1205  	err := runStartTrUnit(t, conn, trTarget)
  1206  	if err == nil {
  1207  		return fmt.Errorf("Unit %s is expected to fail, but succeeded", trTarget.name)
  1208  	}
  1209  
  1210  	unit := getUnitStatusSingle(conn, trTarget.name)
  1211  	if unit != nil && unit.ActiveState == "active" {
  1212  		return fmt.Errorf("Test unit %s is active, should be inactive", trTarget.name)
  1213  	}
  1214  
  1215  	return nil
  1216  }
  1217  
  1218  // Ensure that transient unit with Wants starting and stopping works.
  1219  // It's expected for target to successfully start, even when its child is not started.
  1220  func checkTransientUnitWants(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
  1221  	conn := setupConn(t)
  1222  	defer conn.Close()
  1223  
  1224  	// Start the target unit
  1225  	err := runStartTrUnit(t, conn, trTarget)
  1226  	if err != nil {
  1227  		return err
  1228  	}
  1229  
  1230  	unit := getUnitStatusSingle(conn, trTarget.name)
  1231  	if unit == nil {
  1232  		return fmt.Errorf("Test unit not found in list")
  1233  	} else if unit.ActiveState != "active" {
  1234  		return fmt.Errorf("Test unit not active")
  1235  	}
  1236  
  1237  	// Stop the unit
  1238  	err = runStopUnit(t, conn, trTarget)
  1239  	if err != nil {
  1240  		return err
  1241  	}
  1242  
  1243  	unit = getUnitStatusSingle(conn, trTarget.name)
  1244  	if unit != nil {
  1245  		return fmt.Errorf("Test unit found in list, should be stopped")
  1246  	}
  1247  
  1248  	return nil
  1249  }
  1250  
  1251  // Ensure that transient unit with BindsTo starting and stopping works.
  1252  // Stopping its child should result in stopping target unit.
  1253  func checkTransientUnitBindsTo(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
  1254  	conn := setupConn(t)
  1255  	defer conn.Close()
  1256  
  1257  	// Start the dependent unit
  1258  	err := runStartTrUnit(t, conn, trDep)
  1259  	if err != nil {
  1260  		return err
  1261  	}
  1262  
  1263  	// Start the target unit
  1264  	err = runStartTrUnit(t, conn, trTarget)
  1265  	if err != nil {
  1266  		return err
  1267  	}
  1268  
  1269  	unit := getUnitStatusSingle(conn, trTarget.name)
  1270  	if unit == nil {
  1271  		t.Fatalf("Test unit not found in list")
  1272  	} else if unit.ActiveState != "active" {
  1273  		t.Fatalf("Test unit not active")
  1274  	}
  1275  
  1276  	// Stop the dependent unit
  1277  	err = runStopUnit(t, conn, trDep)
  1278  	if err != nil {
  1279  		return err
  1280  	}
  1281  
  1282  	unit = getUnitStatusSingle(conn, trDep.name)
  1283  	if unit != nil {
  1284  		t.Fatalf("Test unit found in list, should be stopped")
  1285  	}
  1286  
  1287  	// Then the target unit should be gone
  1288  	unit = getUnitStatusSingle(conn, trTarget.name)
  1289  	if unit != nil {
  1290  		t.Fatalf("Test unit found in list, should be stopped")
  1291  	}
  1292  
  1293  	return nil
  1294  }
  1295  
  1296  // Ensure that transient unit with Conflicts starting and stopping works.
  1297  func checkTransientUnitConflicts(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
  1298  	conn := setupConn(t)
  1299  	defer conn.Close()
  1300  
  1301  	// Start the dependent unit
  1302  	err := runStartTrUnit(t, conn, trDep)
  1303  	if err != nil {
  1304  		return err
  1305  	}
  1306  
  1307  	// Start the target unit
  1308  	err = runStartTrUnit(t, conn, trTarget)
  1309  	if err != nil {
  1310  		return err
  1311  	}
  1312  
  1313  	isTargetActive := false
  1314  	unit := getUnitStatusSingle(conn, trTarget.name)
  1315  	if unit != nil && unit.ActiveState == "active" {
  1316  		isTargetActive = true
  1317  	}
  1318  
  1319  	isReqDepActive := false
  1320  	unit = getUnitStatusSingle(conn, trDep.name)
  1321  	if unit != nil && unit.ActiveState == "active" {
  1322  		isReqDepActive = true
  1323  	}
  1324  
  1325  	if isTargetActive && isReqDepActive {
  1326  		return fmt.Errorf("Conflicts didn't take place")
  1327  	}
  1328  
  1329  	// Stop the target unit
  1330  	if isTargetActive {
  1331  		err = runStopUnit(t, conn, trTarget)
  1332  		if err != nil {
  1333  			return err
  1334  		}
  1335  
  1336  		unit = getUnitStatusSingle(conn, trTarget.name)
  1337  		if unit != nil {
  1338  			return fmt.Errorf("Test unit %s found in list, should be stopped", trTarget.name)
  1339  		}
  1340  	}
  1341  
  1342  	// Stop the dependent unit
  1343  	if isReqDepActive {
  1344  		err = runStopUnit(t, conn, trDep)
  1345  		if err != nil {
  1346  			return err
  1347  		}
  1348  
  1349  		unit = getUnitStatusSingle(conn, trDep.name)
  1350  		if unit != nil {
  1351  			return fmt.Errorf("Test unit %s found in list, should be stopped", trDep.name)
  1352  		}
  1353  	}
  1354  
  1355  	return nil
  1356  }
  1357  
  1358  // Ensure that putting running programs into scopes works
  1359  func TestStartStopTransientScope(t *testing.T) {
  1360  	conn := setupConn(t)
  1361  	defer conn.Close()
  1362  
  1363  	cmd := exec.Command("/bin/sleep", "400")
  1364  	err := cmd.Start()
  1365  	if err != nil {
  1366  		t.Fatal(err)
  1367  	}
  1368  	defer cmd.Process.Kill()
  1369  
  1370  	props := []Property{
  1371  		PropPids(uint32(cmd.Process.Pid)),
  1372  	}
  1373  	target := fmt.Sprintf("testing-transient-%d.scope", cmd.Process.Pid)
  1374  
  1375  	// Start the unit
  1376  	reschan := make(chan string)
  1377  	_, err = conn.StartTransientUnit(target, "replace", props, reschan)
  1378  	if err != nil {
  1379  		t.Fatal(err)
  1380  	}
  1381  
  1382  	job := <-reschan
  1383  	if job != "done" {
  1384  		t.Fatal("Job is not done:", job)
  1385  	}
  1386  
  1387  	units, err := conn.ListUnits()
  1388  	if err != nil {
  1389  		t.Fatal(err)
  1390  	}
  1391  
  1392  	unit := getUnitStatus(units, target)
  1393  
  1394  	if unit == nil {
  1395  		t.Fatalf("Test unit not found in list")
  1396  	} else if unit.ActiveState != "active" {
  1397  		t.Fatalf("Test unit not active")
  1398  	}
  1399  
  1400  	// maybe check if pid is really a member of the just created scope
  1401  	//   systemd uses the following api which does not use dbus, but directly
  1402  	//   accesses procfs for cgroup information.
  1403  	//     int sd_pid_get_unit(pid_t pid, char **session)
  1404  }
  1405  
  1406  // Ensure that basic unit gets killed by SIGTERM
  1407  func TestKillUnit(t *testing.T) {
  1408  	target := "start-stop.service"
  1409  	conn := setupConn(t)
  1410  	defer conn.Close()
  1411  
  1412  	err := conn.Subscribe()
  1413  	if err != nil {
  1414  		t.Fatal(err)
  1415  	}
  1416  
  1417  	subSet := conn.NewSubscriptionSet()
  1418  	evChan, errChan := subSet.Subscribe()
  1419  
  1420  	subSet.Add(target)
  1421  
  1422  	setupUnit(target, conn, t)
  1423  	linkUnit(target, conn, t)
  1424  
  1425  	// Start the unit
  1426  	reschan := make(chan string)
  1427  	_, err = conn.StartUnit(target, "replace", reschan)
  1428  	if err != nil {
  1429  		t.Fatal(err)
  1430  	}
  1431  
  1432  	job := <-reschan
  1433  	if job != "done" {
  1434  		t.Fatal("Job is not done:", job)
  1435  	}
  1436  
  1437  	// send SIGTERM
  1438  	conn.KillUnit(target, int32(syscall.SIGTERM))
  1439  
  1440  	timeout := make(chan bool, 1)
  1441  	go func() {
  1442  		time.Sleep(3 * time.Second)
  1443  		close(timeout)
  1444  	}()
  1445  
  1446  	// Wait for the event, expecting the target UnitStatus meets one of the
  1447  	// following conditions:
  1448  	//  * target is nil, meaning the unit has completely gone.
  1449  	//  * target is non-nil, and its ActiveState is not active.
  1450  waitevent:
  1451  	for {
  1452  		select {
  1453  		case changes := <-evChan:
  1454  			tch, ok := changes[target]
  1455  			if !ok {
  1456  				continue waitevent
  1457  			}
  1458  			if tch == nil || (tch != nil && tch.Name == target && tch.ActiveState != "active") {
  1459  				break waitevent
  1460  			}
  1461  		case err = <-errChan:
  1462  			t.Fatal(err)
  1463  		case <-timeout:
  1464  			t.Fatal("Reached timeout")
  1465  		}
  1466  	}
  1467  }
  1468  
  1469  // Ensure that a failed unit gets reset
  1470  func TestResetFailedUnit(t *testing.T) {
  1471  	target := "start-failed.service"
  1472  	conn := setupConn(t)
  1473  	defer conn.Close()
  1474  
  1475  	setupUnit(target, conn, t)
  1476  	linkUnit(target, conn, t)
  1477  
  1478  	// Start the unit
  1479  	reschan := make(chan string)
  1480  	_, err := conn.StartUnit(target, "replace", reschan)
  1481  	if err != nil {
  1482  		t.Fatal(err)
  1483  	}
  1484  
  1485  	job := <-reschan
  1486  	if job != "failed" {
  1487  		t.Fatal("Job is not failed:", job)
  1488  	}
  1489  
  1490  	units, err := conn.ListUnits()
  1491  	if err != nil {
  1492  		t.Fatal(err)
  1493  	}
  1494  
  1495  	unit := getUnitStatus(units, target)
  1496  	if unit == nil {
  1497  		t.Fatalf("Test unit not found in list")
  1498  	}
  1499  
  1500  	// reset the failed unit
  1501  	err = conn.ResetFailedUnit(target)
  1502  	if err != nil {
  1503  		t.Fatal(err)
  1504  	}
  1505  
  1506  	// Ensure that the target unit is actually gone
  1507  	units, err = conn.ListUnits()
  1508  	if err != nil {
  1509  		t.Fatal(err)
  1510  	}
  1511  
  1512  	found := false
  1513  	for _, u := range units {
  1514  		if u.Name == target {
  1515  			found = true
  1516  			break
  1517  		}
  1518  	}
  1519  	if found {
  1520  		t.Fatalf("Test unit still found in list. units = %v", units)
  1521  	}
  1522  }
  1523  
  1524  func TestConnJobListener(t *testing.T) {
  1525  	target := "start-stop.service"
  1526  	conn := setupConn(t)
  1527  	defer conn.Close()
  1528  
  1529  	setupUnit(target, conn, t)
  1530  	linkUnit(target, conn, t)
  1531  
  1532  	jobSize := len(conn.jobListener.jobs)
  1533  
  1534  	reschan := make(chan string)
  1535  	_, err := conn.StartUnit(target, "replace", reschan)
  1536  	if err != nil {
  1537  		t.Fatal(err)
  1538  	}
  1539  
  1540  	<-reschan
  1541  
  1542  	_, err = conn.StopUnit(target, "replace", reschan)
  1543  	if err != nil {
  1544  		t.Fatal(err)
  1545  	}
  1546  
  1547  	<-reschan
  1548  
  1549  	currentJobSize := len(conn.jobListener.jobs)
  1550  	if jobSize != currentJobSize {
  1551  		t.Fatal("JobListener jobs leaked")
  1552  	}
  1553  }
  1554  
  1555  // Enables a unit and then masks/unmasks it
  1556  func TestMaskUnmask(t *testing.T) {
  1557  	target := "mask-unmask.service"
  1558  	conn := setupConn(t)
  1559  	defer conn.Close()
  1560  
  1561  	setupUnit(target, conn, t)
  1562  	abs := findFixture(target, t)
  1563  	runPath := filepath.Join("/run/systemd/system/", target)
  1564  
  1565  	// 1. Enable the unit
  1566  	install, changes, err := conn.EnableUnitFiles([]string{abs}, true, true)
  1567  	if err != nil {
  1568  		t.Fatal(err)
  1569  	}
  1570  
  1571  	if install {
  1572  		t.Log("Install was true")
  1573  	}
  1574  
  1575  	if len(changes) < 1 {
  1576  		t.Fatalf("Expected one change, got %v", changes)
  1577  	}
  1578  
  1579  	if changes[0].Filename != runPath {
  1580  		t.Fatal("Unexpected target filename")
  1581  	}
  1582  
  1583  	// 2. Mask the unit
  1584  	mChanges, err := conn.MaskUnitFiles([]string{target}, true, true)
  1585  	if err != nil {
  1586  		t.Fatal(err)
  1587  	}
  1588  	if mChanges[0].Filename != runPath {
  1589  		t.Fatalf("Change should include correct filename, %+v", mChanges[0])
  1590  	}
  1591  	if mChanges[0].Destination != "" {
  1592  		t.Fatalf("Change destination should be empty, %+v", mChanges[0])
  1593  	}
  1594  
  1595  	// 3. Unmask the unit
  1596  	uChanges, err := conn.UnmaskUnitFiles([]string{target}, true)
  1597  	if err != nil {
  1598  		t.Fatal(err)
  1599  	}
  1600  	if uChanges[0].Filename != runPath {
  1601  		t.Fatalf("Change should include correct filename, %+v", uChanges[0])
  1602  	}
  1603  	if uChanges[0].Destination != "" {
  1604  		t.Fatalf("Change destination should be empty, %+v", uChanges[0])
  1605  	}
  1606  
  1607  }
  1608  
  1609  // Test a global Reload
  1610  func TestReload(t *testing.T) {
  1611  	conn := setupConn(t)
  1612  	defer conn.Close()
  1613  
  1614  	err := conn.Reload()
  1615  	if err != nil {
  1616  		t.Fatal(err)
  1617  	}
  1618  }
  1619  
  1620  func TestUnitName(t *testing.T) {
  1621  	for _, unit := range []string{
  1622  		"",
  1623  		"foo.service",
  1624  		"foobar",
  1625  		"woof@woof.service",
  1626  		"0123456",
  1627  		"account_db.service",
  1628  		"got-dashes",
  1629  	} {
  1630  		got := unitName(unitPath(unit))
  1631  		if got != unit {
  1632  			t.Errorf("bad result for unitName(%s): got %q, want %q", unit, got, unit)
  1633  		}
  1634  	}
  1635  }
  1636  
  1637  func TestFreezer(t *testing.T) {
  1638  	target := "freeze.service"
  1639  	conn := setupConn(t)
  1640  	defer conn.Close()
  1641  
  1642  	setupUnit(target, conn, t)
  1643  	linkUnit(target, conn, t)
  1644  
  1645  	reschan := make(chan string)
  1646  	_, err := conn.StartUnit(target, "replace", reschan)
  1647  	if err != nil {
  1648  		t.Fatal(err)
  1649  	}
  1650  
  1651  	job := <-reschan
  1652  	if job != "done" {
  1653  		t.Fatal("Job is not done:", job)
  1654  	}
  1655  
  1656  	if err := conn.FreezeUnit(context.Background(), target); err != nil {
  1657  		// Don't fail the test if freezing units is not implemented at all (on older systemd versions) or
  1658  		// not supported (on systems running with cgroup v1).
  1659  		e, ok := err.(dbus.Error)
  1660  		if ok && (e.Name == "org.freedesktop.DBus.Error.UnknownMethod" || e.Name == "org.freedesktop.DBus.Error.NotSupported") {
  1661  			t.SkipNow()
  1662  		}
  1663  		t.Fatalf("failed to freeze unit %s: %s", target, err)
  1664  	}
  1665  
  1666  	p, err := conn.GetUnitProperty(target, "FreezerState")
  1667  	if err != nil {
  1668  		t.Fatal(err)
  1669  	}
  1670  
  1671  	v := p.Value.Value().(string)
  1672  	if v != "frozen" {
  1673  		t.Fatalf("unit is not frozen after calling FreezeUnit(), FreezerState=%s", v)
  1674  	}
  1675  
  1676  	if err := conn.ThawUnit(context.Background(), target); err != nil {
  1677  		t.Fatalf("failed to thaw unit %s: %s", target, err)
  1678  	}
  1679  
  1680  	p, err = conn.GetUnitProperty(target, "FreezerState")
  1681  	if err != nil {
  1682  		t.Fatal(err)
  1683  	}
  1684  
  1685  	v = p.Value.Value().(string)
  1686  	if v != "running" {
  1687  		t.Fatalf("unit is not frozen after calling ThawUnit(), FreezerState=%s", v)
  1688  	}
  1689  
  1690  	runStopUnit(t, conn, TrUnitProp{target, nil})
  1691  }
  1692  

View as plain text