1
2
3
4
5
6
7
8
9
10
11
12
13
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
56 conn.StopUnit(target, "replace", nil)
57
58
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
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
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
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
184 _, err = conn.StopUnit(target, "replace", reschan)
185 if err != nil {
186 t.Fatal(err)
187 }
188
189
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
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
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
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
250 _, err = conn.StopUnit(target, "replace", reschan)
251 if err != nil {
252 t.Fatal(err)
253 }
254
255
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
269
270 reschan = make(chan string)
271 _, err = conn.TryRestartUnit(target, "replace", reschan)
272 if err != nil {
273 t.Fatal(err)
274 }
275
276
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
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
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
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
353
354
355
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
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
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
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
421 _, err = conn.StopUnit(target, "replace", reschan)
422 if err != nil {
423 t.Fatal(err)
424 }
425
426
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
440
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
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
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
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
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
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
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
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
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
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
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
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
765
766 default:
767 t.Fatalf("unexpected property value: %v", val)
768 }
769 }
770
771
772
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
802
803
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
822
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
844
845
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
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
1018 func checkTransientUnit(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
1019 conn := setupConn(t)
1020 defer conn.Close()
1021
1022
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
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
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
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
1081 func checkTransientUnitRequires(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
1082 conn := setupConn(t)
1083 defer conn.Close()
1084
1085
1086 err := runStartTrUnit(t, conn, trDep)
1087 if err != nil {
1088 return err
1089 }
1090
1091
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
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
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
1130 func checkTransientUnitRequiresOv(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
1131 conn := setupConn(t)
1132 defer conn.Close()
1133
1134
1135 err := runStartTrUnit(t, conn, trDep)
1136 if err != nil {
1137 return err
1138 }
1139
1140
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
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
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
1179
1180 func checkTransientUnitRequisite(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
1181 conn := setupConn(t)
1182 defer conn.Close()
1183
1184
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
1199
1200 func checkTransientUnitRequisiteOv(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
1201 conn := setupConn(t)
1202 defer conn.Close()
1203
1204
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
1219
1220 func checkTransientUnitWants(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
1221 conn := setupConn(t)
1222 defer conn.Close()
1223
1224
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
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
1252
1253 func checkTransientUnitBindsTo(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
1254 conn := setupConn(t)
1255 defer conn.Close()
1256
1257
1258 err := runStartTrUnit(t, conn, trDep)
1259 if err != nil {
1260 return err
1261 }
1262
1263
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
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
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
1297 func checkTransientUnitConflicts(t *testing.T, trTarget TrUnitProp, trDep TrUnitProp) error {
1298 conn := setupConn(t)
1299 defer conn.Close()
1300
1301
1302 err := runStartTrUnit(t, conn, trDep)
1303 if err != nil {
1304 return err
1305 }
1306
1307
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
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
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
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
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
1401
1402
1403
1404 }
1405
1406
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
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
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
1447
1448
1449
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
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
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
1501 err = conn.ResetFailedUnit(target)
1502 if err != nil {
1503 t.Fatal(err)
1504 }
1505
1506
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
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
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
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
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
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
1658
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