author | wenzelm |
Tue, 10 Dec 2019 21:05:07 +0100 | |
changeset 71268 | e2fb60756fb8 |
parent 71265 | 6ca561001244 |
child 71280 | 5a2033fc8f3d |
permissions | -rw-r--r-- |
70965 | 1 |
/* Title: Pure/System/linux.scala |
2 |
Author: Makarius |
|
3 |
||
4 |
Specific support for Linux, notably Ubuntu/Debian. |
|
5 |
*/ |
|
6 |
||
7 |
package isabelle |
|
8 |
||
9 |
||
10 |
import scala.util.matching.Regex |
|
11 |
||
12 |
||
13 |
object Linux |
|
14 |
{ |
|
71265 | 15 |
/* required packages */ |
16 |
||
17 |
val packages: List[String] = List("pwgen") |
|
18 |
||
19 |
||
70965 | 20 |
/* check system */ |
21 |
||
22 |
def check_system(): Unit = |
|
23 |
if (!Platform.is_linux) error("Not a Linux system") |
|
24 |
||
25 |
def check_system_root(): Unit = |
|
26 |
{ |
|
27 |
check_system() |
|
28 |
if (Isabelle_System.bash("id -u").check.out != "0") error("Not running as superuser (root)") |
|
29 |
} |
|
30 |
||
31 |
||
32 |
/* release */ |
|
33 |
||
34 |
object Release |
|
35 |
{ |
|
36 |
private val ID = """^Distributor ID:\s*(\S.*)$""".r |
|
37 |
private val RELEASE = """^Release:\s*(\S.*)$""".r |
|
38 |
private val DESCRIPTION = """^Description:\s*(\S.*)$""".r |
|
39 |
||
40 |
def apply(): Release = |
|
41 |
{ |
|
42 |
val lines = Isabelle_System.bash("lsb_release -a").check.out_lines |
|
43 |
def find(R: Regex): String = lines.collectFirst({ case R(a) => a }).getOrElse("Unknown") |
|
44 |
new Release(find(ID), find(RELEASE), find(DESCRIPTION)) |
|
45 |
} |
|
46 |
} |
|
47 |
||
48 |
final class Release private(val id: String, val release: String, val description: String) |
|
49 |
{ |
|
50 |
override def toString: String = description |
|
51 |
||
52 |
def is_ubuntu: Boolean = id == "Ubuntu" |
|
71268 | 53 |
def is_ubuntu_18_04: Boolean = is_ubuntu && release == "18.04" |
70965 | 54 |
} |
70966 | 55 |
|
56 |
||
57 |
/* packages */ |
|
58 |
||
59 |
def reboot_required(): Boolean = |
|
60 |
Path.explode("/var/run/reboot-required").is_file |
|
61 |
||
62 |
def check_reboot_required(): Unit = |
|
63 |
if (reboot_required()) error("Reboot required") |
|
64 |
||
65 |
def package_update(progress: Progress = No_Progress): Unit = |
|
66 |
progress.bash( |
|
67 |
"""apt-get update -y && apt-get upgrade -y && apt autoremove -y""", |
|
68 |
echo = true).check |
|
69 |
||
70 |
def package_install(packages: List[String], progress: Progress = No_Progress): Unit = |
|
71 |
progress.bash("apt-get install -y -- " + Bash.strings(packages), echo = true).check |
|
71046 | 72 |
|
73 |
||
74 |
/* users */ |
|
75 |
||
76 |
def user_exists(name: String): Boolean = |
|
77 |
Isabelle_System.bash("id " + Bash.string(name)).ok |
|
78 |
||
79 |
def user_entry(name: String, field: Int): String = |
|
80 |
{ |
|
81 |
val result = Isabelle_System.bash("getent passwd " + Bash.string(name)).check |
|
82 |
val fields = space_explode(':', result.out) |
|
83 |
||
84 |
if (1 <= field && field <= fields.length) fields(field - 1) |
|
85 |
else error("No passwd field " + field + " for user " + quote(name)) |
|
86 |
} |
|
87 |
||
88 |
def user_description(name: String): String = user_entry(name, 5).takeWhile(_ != ',') |
|
89 |
||
90 |
def user_home(name: String): String = user_entry(name, 6) |
|
91 |
||
71054
b64fc38327ae
prefer system user setup, e.g. avoid occurrence on login screen;
wenzelm
parents:
71051
diff
changeset
|
92 |
def user_add(name: String, |
b64fc38327ae
prefer system user setup, e.g. avoid occurrence on login screen;
wenzelm
parents:
71051
diff
changeset
|
93 |
description: String = "", |
b64fc38327ae
prefer system user setup, e.g. avoid occurrence on login screen;
wenzelm
parents:
71051
diff
changeset
|
94 |
system: Boolean = false, |
b64fc38327ae
prefer system user setup, e.g. avoid occurrence on login screen;
wenzelm
parents:
71051
diff
changeset
|
95 |
ssh_setup: Boolean = false) |
71046 | 96 |
{ |
97 |
require(!description.contains(',')) |
|
98 |
||
99 |
if (user_exists(name)) error("User already exists: " + quote(name)) |
|
100 |
||
101 |
Isabelle_System.bash( |
|
102 |
"adduser --quiet --disabled-password --gecos " + Bash.string(description) + |
|
71054
b64fc38327ae
prefer system user setup, e.g. avoid occurrence on login screen;
wenzelm
parents:
71051
diff
changeset
|
103 |
(if (system) " --system --group --shell /bin/bash " else "") + |
71046 | 104 |
" " + Bash.string(name)).check |
105 |
||
106 |
if (ssh_setup) { |
|
107 |
val id_rsa = user_home(name) + "/.ssh/id_rsa" |
|
108 |
Isabelle_System.bash(""" |
|
109 |
if [ ! -f """ + Bash.string(id_rsa) + """ ] |
|
110 |
then |
|
111 |
yes '\n' | sudo -i -u """ + Bash.string(name) + |
|
112 |
""" ssh-keygen -q -f """ + Bash.string(id_rsa) + """ |
|
113 |
fi |
|
114 |
""").check |
|
115 |
} |
|
116 |
} |
|
71048 | 117 |
|
118 |
||
119 |
/* system services */ |
|
120 |
||
71107 | 121 |
def service_operation(op: String, name: String): Unit = |
122 |
Isabelle_System.bash("systemctl " + Bash.string(op) + " " + Bash.string(name)).check |
|
71048 | 123 |
|
71107 | 124 |
def service_enable(name: String) { service_operation("enable", name) } |
125 |
def service_disable(name: String) { service_operation("disable", name) } |
|
126 |
def service_start(name: String) { service_operation("start", name) } |
|
127 |
def service_stop(name: String) { service_operation("stop", name) } |
|
128 |
def service_restart(name: String) { service_operation("restart", name) } |
|
71051 | 129 |
|
71111
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
wenzelm
parents:
71108
diff
changeset
|
130 |
def service_shutdown(name: String) |
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
wenzelm
parents:
71108
diff
changeset
|
131 |
{ |
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
wenzelm
parents:
71108
diff
changeset
|
132 |
try { service_stop(name) } |
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
wenzelm
parents:
71108
diff
changeset
|
133 |
catch { case ERROR(_) => } |
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
wenzelm
parents:
71108
diff
changeset
|
134 |
} |
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
wenzelm
parents:
71108
diff
changeset
|
135 |
|
71048 | 136 |
def service_install(name: String, spec: String) |
137 |
{ |
|
71111
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
wenzelm
parents:
71108
diff
changeset
|
138 |
service_shutdown(name) |
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
wenzelm
parents:
71108
diff
changeset
|
139 |
|
71048 | 140 |
val service_file = Path.explode("/lib/systemd/system") + Path.basic(name).ext("service") |
141 |
File.write(service_file, spec) |
|
71115 | 142 |
Isabelle_System.chmod("644", service_file) |
71048 | 143 |
|
71107 | 144 |
service_enable(name) |
71108 | 145 |
service_restart(name) |
71048 | 146 |
} |
71265 | 147 |
|
148 |
||
149 |
/* passwords */ |
|
150 |
||
151 |
def generate_password(length: Int = 10): String = |
|
152 |
{ |
|
153 |
require(length >= 6) |
|
154 |
Isabelle_System.bash("pwgen " + length + " 1").check.out |
|
155 |
} |
|
70965 | 156 |
} |