5db01d3dfd7713a4d9cba5c3a59030482814bccd
[tinc] / test / integration / commandline.py
1 #!/usr/bin/env python3
2
3 """Test supported and unsupported commandline flags."""
4
5 import os
6 import shutil
7 import signal
8 import subprocess as subp
9 import tempfile
10 import time
11
12 from testlib import check, util, path
13 from testlib.log import log
14 from testlib.proc import Tinc, Script
15 from testlib.test import Test
16 from testlib.feature import SANDBOX_LEVEL
17
18
19 def init(ctx: Test) -> Tinc:
20     """Initialize new test nodes."""
21     tinc = ctx.node()
22     stdin = f"""
23         init {tinc}
24         set Port 0
25         set Address localhost
26         set DeviceType dummy
27         set Sandbox {SANDBOX_LEVEL}
28     """
29     tinc.cmd(stdin=stdin)
30     tinc.add_script(Script.TINC_UP)
31     return tinc
32
33
34 with Test("commandline flags") as context:
35     node = init(context)
36     pf = node.pid_file
37
38     tincd_flags = (
39         (0, ("-D",)),
40         (0, ("--no-detach",)),
41         (0, ("-D", "-d")),
42         (0, ("-D", "-d2")),
43         (0, ("-D", "-d", "2")),
44         (0, ("-D", "-n", "foo")),
45         (0, ("-D", "-nfoo")),
46         (0, ("-D", "--net=foo")),
47         (0, ("-D", "--net", "foo")),
48         (0, ("-D", "-c", ".", "-c", ".")),
49         (0, ("-D", "-n", "net", "-n", "net")),
50         (0, ("-D", "-n", "net", "-o", "FakeOpt=42")),
51         (0, ("-D", "--logfile=log", "--logfile=log")),
52         (0, ("-D", f"--pidfile={pf}", f"--pidfile={pf}")),
53         (1, ("foo",)),
54         (1, ("--pidfile",)),
55         (1, ("--foo",)),
56         (1, ("-n", "net", "-o", "Compression=")),
57         (1, ("-c", "fakedir", "-n", "n/e\\t")),
58     )
59
60     for code, flags in tincd_flags:
61         COOKIE = util.random_string(10)
62         server = node.tincd(*flags, env={"COOKIE": COOKIE})
63
64         if not code:
65             log.info("waiting for tincd to come up")
66             env = node[Script.TINC_UP].wait().env
67             check.equals(COOKIE, env["COOKIE"])
68
69         log.info("stopping tinc")
70         node.cmd("stop", code=code)
71
72         log.info("reading tincd output")
73         stdout, stderr = server.communicate()
74
75         log.debug('got code %d, ("%s", "%s")', server.returncode, stdout, stderr)
76         check.equals(code, server.returncode)
77
78     tinc_flags = (
79         (0, ("get", "name")),
80         (0, ("-n", "foo", "get", "name")),
81         (0, ("-nfoo", "get", "name")),
82         (0, ("--net=foo", "get", "name")),
83         (0, ("--net", "foo", "get", "name")),
84         (0, ("-c", "conf", "-c", "conf")),
85         (0, ("-n", "net", "-n", "net")),
86         (0, (f"--pidfile={pf}", f"--pidfile={pf}")),
87         (1, ("-n", "foo", "get", "somethingreallyunknown")),
88         (1, ("--net",)),
89         (1, ("--net", "get", "name")),
90         (1, ("foo",)),
91         (1, ("-c", "conf", "-n", "n/e\\t")),
92     )
93
94     for code, flags in tinc_flags:
95         node.cmd(*flags, code=code)
96
97
98 def test_relative_path(ctx: Test, chroot: bool) -> None:
99     """Test tincd with relative paths."""
100
101     foo = init(ctx)
102     confdir = os.path.realpath(foo.sub("."))
103
104     # Workaround for the 108-char limit on UNIX socket path length.
105     shortcut = tempfile.mkdtemp()
106     os.symlink(confdir, os.path.join(shortcut, "conf"))
107
108     log.info("using paths: confdir '%s', shortcut '%s'", confdir, shortcut)
109
110     args = [
111         path.TINCD_PATH,
112         "-D",
113         "-c",
114         "conf",
115         "--pidfile",
116         "pid",
117         "--logfile",
118         "conf/.//./log",
119     ]
120
121     if chroot:
122         args.append("-R")
123
124     pidfile = os.path.join(shortcut, "pid")
125     logfile = os.path.join(confdir, "log")
126
127     with subp.Popen(args, stderr=subp.STDOUT, cwd=shortcut) as tincd:
128         foo[Script.TINC_UP].wait(10)
129
130         log.info("pidfile and logfile must exist at expected paths")
131         check.file_exists(pidfile)
132         check.file_exists(logfile)
133
134         # chrooted tincd won't be able to reopen its log since in this
135         # test we put the log outside tinc's configuration directory.
136         if os.name != "nt" and not chroot:
137             log.info("test log file rotation")
138             time.sleep(1)
139             util.remove_file(logfile)
140             os.kill(tincd.pid, signal.SIGHUP)
141             time.sleep(1)
142
143             log.info("pidfile and logfile must still exist")
144             check.file_exists(pidfile)
145             check.file_exists(logfile)
146
147         log.info("stopping tinc through '%s'", pidfile)
148         foo.cmd("--pidfile", pidfile, "stop")
149         check.equals(0, tincd.wait())
150
151     # Leave behind as debugging aid if there's an exception
152     shutil.rmtree(shortcut)
153
154
155 with Test("relative path to tincd dir") as context:
156     test_relative_path(context, chroot=False)
157
158 if os.name != "nt" and not os.getuid():
159     with Test("relative path to tincd dir (chroot)") as context:
160         test_relative_path(context, chroot=True)