aboutsummaryrefslogtreecommitdiff
path: root/init
blob: 7b869cb496df8263a1d93e1c9469d989f2d408e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
#!/bin/bash

# Init script for build2 buildos.
#
# Loosely based on the one that comes in Debian initrd.img (since we are
# using its kernel image as is).
#
trap "exit 1" ERR
set -o errtrace # Trap in functions.

# Note: diagnostics goes to stdout.
#
function info () { echo "$*"; }
function error ()
{
  if [ "$#" -gt 0 ]; then
    info "$*";
  fi

  # The setsid voodoo (take from Debian init's panic()) is to enable job
  # control.
  #
  info "type Ctrl-D to exit shell and reboot"
  setsid /bin/bash -c "exec /bin/bash -i <>/dev/tty1 1>&0 2>&1"
  reboot
}

# Some pre-systemd utilities (like reboot) come from klibc-utils.
#
export PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/lib/klibc/bin/

# One would expect rootflags=size=1g to work but it doesn't (perhaps init
# is expected to interpret it)?
#
mount -o remount,size=1G /

mkdir -p /sys /proc
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
mount -t proc -o nodev,noexec,nosuid proc /proc

info "init starting up..."

mount -t devtmpfs -o nosuid,mode=0755 udev /dev
mkdir -p /dev/pts
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts || true

mkdir -p /run
mount -t tmpfs -o "noexec,nosuid,size=10%,mode=0755" tmpfs /run

mkdir -p /tmp
mount -t tmpfs -o "nodev,nosuid,size=10%,mode=1777" tmpfs /tmp

# Start udev.
#
# Based on Debian initrd's init-top/udev. Note that we don't stop it at
# at the end of init.
#
info "starting udev..."

if [ -w /sys/kernel/uevent_helper ]; then
  echo >/sys/kernel/uevent_helper
fi

SYSTEMD_LOG_LEVEL=info /lib/systemd/systemd-udevd --daemon --resolve-names=never

udevadm trigger --action=add
udevadm settle || true

# Parse the kernel command line. This is complicated by the fact that the
# values can be quoted, for example:
#
# foo='foo fox'
# bar="bar 'box'"
#
# First we separete quoted variables and arguments with newlines (giving
# priority to assignments). Then we replace whitespaces with newline on
# lines that don't contain quites. Finally, clean up by removing blank
# lines.
#
# Note: the same code as in buildos.
#
readarray -t cmdline < <(cat /proc/cmdline | \
  sed -r -e "s/([^ ]+=)?('[^']*'|\"[^\"]*\")/\n\1\2\n/g" | \
  sed -r -e "/['\"]/!s/ /\n/g" |
  sed -r -e '/^\s*$/d')

# Enter all buildos variables as bash variables.
#
info "command line:"
for v in "${cmdline[@]}"; do
  var="$(sed -r -n -e 's/^buildos\.([^=]+)=.*$/\1/p' <<<"$v")" # Extract name.

  if [ -n "$var" ]; then
    val="$(sed -r -e 's/^[^=]+=(.*)$/\1/' <<<"$v")"            # Extract value.
    val="$(sed -r -e "s/^('(.*)'|\"(.*)\")$/\2\3/" <<<"$val")" # Strip quoted.
    info "  $var=$val"
    declare "$var=$val"
  fi
done

# Figure out network configuration and generate the corresponding
# /etc/network/interfaces.
#
info "starting network..."

# We are using udev's predictable interface names. The two character prefixes
# based on the type of interface:
#
#  en -- ethernet
#  sl -- serial line IP (slip)
#  wl -- wlan
#  ww -- wwan
#
eth_all="$(cd /sys/class/net && ls -d en?*)"

if [ -z "$eth_all" ]; then
  info "no ethernet interfaces found among:"
  ip link show
  error
fi

eth=
eth_up=
for s in 1 2 4 8; do

  # Try to bring them all up and find the one that has carrier.
  #
  for i in $eth_all; do
    ip link set "$i" up || true
  done

  sleep "$s"

  for i in $eth_all; do
    if [ "$(cat "/sys/class/net/$i/carrier")" -eq "1" ]; then
      info "detected carrier on $i"
      eth_up+=" $i"
    fi
  done

  # Bring them all down.
  #
  for i in $eth_all; do
    ip link set "$i" down || true
  done

  # If we didn't find anything, try to wait for carrier longer.
  #
  if [ -z "$eth_up" ]; then
    continue
  fi

  # If we end up with several interfaces we simply unleash dhcp on all of
  # them and use the first that gets configured.
  #
  # Note also that it's possible the interface that we want is not yet ready
  # in which case we will try to wait for carrier a bit longer.
  #
  for i in $eth_up; do
    if dhclient -v "$i"; then
      eth="$i"
      break
    fi
  done

  if [ -n "$eth" ]; then
    break
  fi
done

if [ -z "$eth_up" ]; then
  info "no ethernet interfaces with carrier among:"
  ip link show
  error
fi

if [ -z "$eth" ]; then
  info "no ethernet interfaces with DHCP among:"
  ip link show
  error
fi

mac="$(cat "/sys/class/net/$eth/address")"
mid="$(sed -e 's/://g' <<<"$mac")" # Machine id.

info "configured $eth ($mac)"

# Set the hostname.
#
hname="$(hostname)"
if [ "$hname" = "(none)" ]; then
  hname="build-$mid"
  hostname "$hname"
fi
echo "$hname" >/etc/hostname

info "hostname $hname"

# Stop DHCP client without releasing the lease and deconfigure the interface.
# The plan is to generate a bridge-based /etc/network/interfaces configuration
# based on what we have discovered and then let the systemd networking bringup
# to configure everything (at which point we will hopefully reuse the lease).
#
dhclient -x 2>/dev/null

# @@ Need to be make configurable.
#
priv_network="172.16.123.0"
priv_netmask="255.255.255.0"
priv_netbase="$(sed -e 's/^\(.*\)\.0$/\1/' <<<"$priv_network")"

cat <<EOF >/etc/network/interfaces
auto lo
iface lo inet loopback

# Public bridge.
#
auto br0
iface br0 inet dhcp
    bridge_ports   $eth
    bridge_stp     off
    bridge_maxwait 0
    bridge_fd      0
    bridge_mac     $mac

# Private bridge with NAT to br0.
#
auto br1
iface br1 inet static
    address ${priv_netbase}.1
    netmask $priv_netmask
    bridge_ports   none
    bridge_stp     off
    bridge_maxwait 0
    bridge_fd      0
    post-up iptables -t nat -A POSTROUTING -o br0 -j MASQUERADE
    post-up iptables -A FORWARD -i br0 -o br1 -m state --state RELATED,ESTABLISHED -j ACCEPT
    post-up iptables -A FORWARD -i br1 -o br0 -j ACCEPT
EOF

cat <<EOF >/etc/dnsmasq.d/br1-dhcp
interface=br1
bind-interfaces
dhcp-range=${priv_netbase}.10,${priv_netbase}.250,12h
EOF

# Configure Postfix.
#
cat <<<"$hname" >/etc/mailname

sed -r -i \
 -e "s%^(myhostname).*%\1 = $hname%" \
 -e 's%^(mydestination).*%\1 = $myhostname, localhost.localdomain, localhost%' \
 -e 's%^(mynetworks).*%\1 = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128%' \
 -e "s%^(relayhost).*%\1 = $smtp_relay%" \
 /etc/postfix/main.cf

# Make admin alias for buildos.admin_email, alias root as admin.
#
cat <<EOF >>/etc/aliases
admin: $admin_email
root: admin
EOF
newaliases

# Figure out disk configuration and generate the corresponding /etc/fstab.
#
fstab=/etc/fstab
#fstab=/dev/stdout

echo -n '' >$fstab

l=
machines=
while read l || [ -n "$l" ]; do
  d="$(sed -re 's/.*NAME=\"([^\"]+)\".*/\1/'   <<<"$l")"
  t="$(sed -re 's/.*FSTYPE=\"([^\"]*)\".*/\1/' <<<"$l")"
  l="$(sed -re 's/.*LABEL=\"([^\"]*)\".*/\1/'  <<<"$l")"

  # Strip the buildos. prefix from the label. If the result is empty then
  # this disk/patition hasn't been labeled for use by buildos.
  #
  l="$(sed -n -re 's/^buildos\.([^ ]+)$/\1/p' <<<"$l")"

  if [ -z "$l" ]; then
    continue
  fi

  # Handle buildos.machines and buildos.machines.* mounts.
  #
  if [[ "$l" == "machines" ]] || [[ "$l" =~ "machines.".+ ]]; then

    if [ "$t" != "btrfs" ]; then
      error "non-btrfs filesystem on $d labeled with buildos.machines"
    fi

    if [ "$l" = "machines" ]; then
      # Single mount.
      #
      if [ -n "$machines" ]; then
	error "multiple disks labeled with buildos.machines/machines.*"
      fi

      m=/build/machines
      machines="single"
    else
      # Multiple mounts.
      #
      if [ "$machines" = "single" ]; then
	error "multiple disks labeled with buildos.machines/machines.*"
      fi

      n="$(sed -n -re 's/^machines\.([^ ]+)$/\1/p' <<<"$l")"
      m="/build/machines/$n"
      machines="multiple"
    fi

    info "mounting $d (buildos.$l) on $m"

    echo mkdir -p "$m"
    o="defaults,noatime,nodiratime,user_subvol_rm_allowed"
    echo "$d $m btrfs $o 0 0" >>$fstab
  fi
done < <(lsblk --pairs --paths --output NAME,FSTYPE,LABEL)
#done <<EOF
#NAME="/dev/sda" FSTYPE="btrfs" LABEL="buildos.machines.vol1"
#NAME="/dev/sdb" FSTYPE="btrfs" LABEL="buildos.machines.vol2"
#EOF

if [ -z "$machines" ]; then
  info "no disks labaled with buildos.machines* among:"
  lsblk --paths --output NAME,TYPE,FSTYPE,SIZE,LABEL,UUID
  info "consider formatting and/or labelling a suitable disk"
  error
fi

/bin/bash

# Hand off to systemd. But first arrange to keep console output (which
# becomes tty1).
#
mkdir -p /etc/systemd/system/getty@tty1.service.d
cat <<EOF >/etc/systemd/system/getty@tty1.service.d/noclear.conf
[Service]
TTYVTDisallocate=no
EOF

export PATH=/sbin:/usr/sbin:/bin:/usr/bin

exec /lib/systemd/systemd \
  --show-status=1 \
  --machine-id="00000000000000000000$mid" \
  </dev/console >/dev/console 2>&1