Print this page
NEX-7823 ipmgmtd can't properly remove interface from the old ipadm.conf format
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/svc/milestone/net-physical
+++ new/usr/src/cmd/svc/milestone/net-physical
1 1 #!/sbin/sh
2 2 #
3 3 # CDDL HEADER START
4 4 #
5 5 # The contents of this file are subject to the terms of the
6 6 # Common Development and Distribution License (the "License").
7 7 # You may not use this file except in compliance with the License.
8 8 #
9 9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 10 # or http://www.opensolaris.org/os/licensing.
11 11 # See the License for the specific language governing permissions
|
↓ open down ↓ |
11 lines elided |
↑ open up ↑ |
12 12 # and limitations under the License.
13 13 #
14 14 # When distributing Covered Code, include this CDDL HEADER in each
15 15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 16 # If applicable, add the following below this CDDL HEADER, with the
17 17 # fields enclosed by brackets "[]" replaced with your own identifying
18 18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 19 #
20 20 # CDDL HEADER END
21 21 #
22 +
22 23 #
24 +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T.
25 +# All rights reserved.
23 26 # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
24 27 # Copyright 2012 Milan Jurik. All rights reserved.
28 +# Copyright 2016 Nexenta Systems, Inc.
25 29 #
26 -# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T.
27 -# All rights reserved.
28 -#
29 30
30 31 . /lib/svc/share/smf_include.sh
31 32 . /lib/svc/share/net_include.sh
32 33
33 34 #
34 35 # In a shared-IP zone we need this service to be up, but all of the work
35 36 # it tries to do is irrelevant (and will actually lead to the service
36 37 # failing if we try to do it), so just bail out.
37 38 # In the global zone and exclusive-IP zones we proceed.
38 39 #
39 40 smf_configure_ip || exit $SMF_EXIT_OK
40 41
41 42
42 43 # Make sure that the libraries essential to this stage of booting can be found.
43 44 LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH
44 45
45 46 smf_netstrategy
46 47
47 48 if smf_is_globalzone; then
48 49 net_reconfigure || exit $SMF_EXIT_ERR_CONFIG
49 50
50 51 # Update PVID on interfaces configured with VLAN 1
51 52 update_pvid
52 53
53 54 #
54 55 # Upgrade handling. The upgrade file consists of a series of dladm(1M)
55 56 # commands. Note that after we are done, we cannot rename the upgrade
56 57 # script file as the file system is still read-only at this point.
57 58 # Defer this to the manifest-import service.
58 59 #
59 60 upgrade_script=/var/svc/profile/upgrade_datalink
60 61 if [ -f "${upgrade_script}" ]; then
61 62 . "${upgrade_script}"
62 63 fi
63 64
64 65 #
65 66 # Upgrade handling for ibd:
66 67 # After we are done with the upgrade handling, we can not set the
67 68 # ibd/ibd_upgraded property to "true" as the file system is
68 69 # read-only at this point. It will be done later by ibd-post-upgrade
69 70 # service.
70 71 #
71 72 if [ -x /sbin/ibd_upgrade ]; then
72 73 ibd_upgraded=`/bin/svcprop -c -p ibd/ibd_upgraded \
73 74 $SMF_FMRI 2> /dev/null`
74 75 if [ "$ibd_upgraded" != "true" ]; then
75 76 /sbin/ibd_upgrade -v
76 77 fi
77 78 fi
78 79
79 80 #
80 81 # Bring up simnets, link aggregations and initialize security objects.
81 82 # Note that link property initialization is deferred until after
82 83 # IP interfaces are plumbed to ensure that the links will not
83 84 # be unloaded (and the property settings lost). We should bring
84 85 # up simnets prior to VLANs/Aggrs to enable creation of VLANs/Aggrs
85 86 # over simnets.
86 87 #
87 88 /sbin/dladm up-simnet
88 89 /sbin/dladm up-aggr
89 90 /sbin/dladm up-vlan
90 91 /sbin/dladm up-part
91 92 /sbin/dladm init-secobj
92 93 #
93 94 # Bring up VNICs
94 95 #
95 96 /sbin/dladm up-vnic
96 97 #
97 98 # Create flows via flowadm.
98 99 #
99 100 /sbin/flowadm init-flow
100 101 fi
101 102
102 103 #
103 104 # If the system was net booted by DHCP, hand DHCP management off to the
104 105 # DHCP agent (ifconfig communicates to the DHCP agent through the
105 106 # loopback interface).
106 107 #
107 108 if [ -n "$_INIT_NET_IF" -a "$_INIT_NET_STRATEGY" = "dhcp" ]; then
108 109 /sbin/dhcpagent -a
109 110 fi
110 111
111 112 #
112 113 # The network initialization is done early to support diskless and
113 114 # dataless configurations. For IPv4 interfaces that were configured by
114 115 # the kernel (e.g. those on diskless machines) and not configured by
115 116 # DHCP, reset the netmask using the local "/etc/netmasks" file if one
116 117 # exists, and then reset the broadcast address based on the netmask.
117 118 #
118 119 /sbin/ifconfig -auD4 netmask + broadcast +
119 120
120 121 is_iptun ()
121 122 {
122 123 intf=$1
123 124 # Is this a persistent IP tunnel link?
124 125 /sbin/dladm show-iptun -P $intf > /dev/null 2>&1
125 126 if [ $? -eq 0 ]; then
126 127 return 0
127 128 fi
128 129 # Is this an implicit IP tunnel (i.e., ip.tun0)
129 130 ORIGIFS="$IFS"
130 131 IFS="$IFS."
131 132 set -- $intf
132 133 IFS="$ORIGIFS"
133 134 if [ $# -eq 2 -a \( "$1" = "ip" -o "$1" = "ip6" \) ]; then
134 135 #
135 136 # It looks like one, but another type of link might be
136 137 # using a name that looks like an implicit IP tunnel.
137 138 # If dladm show-link -P finds it, then it's not an IP
138 139 # tunnel.
139 140 #
140 141 /sbin/dladm show-link -Pp $intf > /dev/null 2>&1
141 142 if [ $? -eq 0 ]; then
142 143 return 1
143 144 else
144 145 return 0
145 146 fi
146 147 fi
147 148 return 1
148 149 }
149 150
150 151 #
151 152 # All the IPv4 and IPv6 interfaces are plumbed before doing any
152 153 # interface configuration. This prevents errors from plumb failures
153 154 # getting mixed in with the configured interface lists that the script
154 155 # outputs.
155 156 #
156 157
157 158 #
158 159 # First deal with /etc/hostname
159 160 #
160 161 # Get the list of IPv4 interfaces to configure by breaking
161 162 # /etc/hostname.* into separate args by using "." as a shell separator
162 163 # character.
163 164 #
164 165 interface_names="`echo /etc/hostname.*[0-9] 2>/dev/null`"
165 166 if [ "$interface_names" != "/etc/hostname.*[0-9]" ]; then
166 167 ORIGIFS="$IFS"
167 168 IFS="$IFS."
168 169 set -- $interface_names
169 170 IFS="$ORIGIFS"
170 171 while [ $# -ge 2 ]; do
171 172 shift
172 173 intf_name=$1
173 174 while [ $# -gt 1 -a "$2" != "/etc/hostname" ]; do
174 175 intf_name="$intf_name.$2"
175 176 shift
176 177 done
177 178 shift
178 179
179 180 # skip IP tunnel interfaces plumbed by net-iptun.
180 181 if is_iptun $intf_name; then
181 182 continue
182 183 fi
183 184
184 185 read one rest < /etc/hostname.$intf_name
185 186 if [ "$one" = ipmp ]; then
186 187 ipmp_list="$ipmp_list $intf_name"
187 188 else
188 189 inet_list="$inet_list $intf_name"
189 190 fi
190 191 done
191 192 fi
192 193
193 194 #
194 195 # Get the list of IPv6 interfaces to configure by breaking
195 196 # /etc/hostname6.* into separate args by using "." as a shell separator
196 197 # character.
197 198 #
198 199 interface_names="`echo /etc/hostname6.*[0-9] 2>/dev/null`"
199 200 if [ "$interface_names" != "/etc/hostname6.*[0-9]" ]; then
200 201 ORIGIFS="$IFS"
201 202 IFS="$IFS."
202 203 set -- $interface_names
203 204 IFS="$ORIGIFS"
204 205 while [ $# -ge 2 ]; do
205 206 shift
206 207 intf_name=$1
207 208 while [ $# -gt 1 -a "$2" != "/etc/hostname6" ]; do
208 209 intf_name="$intf_name.$2"
209 210 shift
210 211 done
211 212 shift
212 213
213 214 # skip IP tunnel interfaces plumbed by net-iptun.
214 215 if is_iptun $intf_name; then
215 216 continue
216 217 fi
217 218
218 219 read one rest < /etc/hostname6.$intf_name
219 220 if [ "$one" = ipmp ]; then
220 221 ipmp6_list="$ipmp6_list $intf_name"
221 222 else
222 223 inet6_list="$inet6_list $intf_name"
223 224 fi
224 225 done
225 226 fi
226 227
227 228 #
228 229 # Create all of the IPv4 IPMP interfaces.
229 230 #
230 231 if [ -n "$ipmp_list" ]; then
231 232 set -- $ipmp_list
232 233 while [ $# -gt 0 ]; do
233 234 if /sbin/ifconfig $1 ipmp; then
234 235 ipmp_created="$ipmp_created $1"
235 236 else
236 237 ipmp_failed="$ipmp_failed $1"
237 238 fi
238 239 shift
239 240 done
240 241 [ -n "$ipmp_failed" ] && warn_failed_ifs "create IPv4 IPMP" \
241 242 "$ipmp_failed"
242 243 fi
243 244
244 245 #
245 246 # Step through the IPv4 interface list and try to plumb every interface.
246 247 # Generate list of plumbed and failed IPv4 interfaces.
247 248 #
248 249 if [ -n "$inet_list" ]; then
249 250 set -- $inet_list
250 251 while [ $# -gt 0 ]; do
251 252 /sbin/ifconfig $1 plumb
252 253 if /sbin/ifconfig $1 inet >/dev/null 2>&1; then
253 254 inet_plumbed="$inet_plumbed $1"
254 255 else
255 256 inet_failed="$inet_failed $1"
256 257 fi
257 258 shift
258 259 done
259 260 [ -n "$inet_failed" ] && warn_failed_ifs "plumb IPv4" "$inet_failed"
260 261 fi
261 262
262 263 # Run autoconf to connect to a WLAN if the interface is a wireless one
263 264 if [ -x /sbin/wificonfig -a -n "$inet_plumbed" ]; then
264 265 set -- $inet_plumbed
265 266 while [ $# -gt 0 ]; do
266 267 if [ -r /dev/wifi/$1 ]; then
267 268 /sbin/wificonfig -i $1 startconf >/dev/null
268 269 fi
269 270 shift
270 271 done
271 272 fi
272 273
273 274 #
274 275 # Step through the IPv6 interface list and plumb every interface.
275 276 # Generate list of plumbed and failed IPv6 interfaces. Each plumbed
276 277 # interface will be brought up later, after processing any contents of
277 278 # the /etc/hostname6.* file.
278 279 #
279 280 if [ -n "$inet6_list" ]; then
280 281 set -- $inet6_list
281 282 while [ $# -gt 0 ]; do
282 283 /sbin/ifconfig $1 inet6 plumb
283 284 if /sbin/ifconfig $1 inet6 >/dev/null 2>&1; then
284 285 inet6_plumbed="$inet6_plumbed $1"
285 286 else
286 287 inet6_failed="$inet6_failed $1"
287 288 fi
288 289 shift
289 290 done
290 291 [ -n "$inet6_failed" ] && warn_failed_ifs "plumb IPv6" "$inet6_failed"
291 292 fi
292 293
293 294 #
294 295 # Create all of the IPv6 IPMP interfaces.
295 296 #
296 297 if [ -n "$ipmp6_list" ]; then
297 298 set -- $ipmp6_list
298 299 while [ $# -gt 0 ]; do
299 300 if /sbin/ifconfig $1 inet6 ipmp; then
|
↓ open down ↓ |
261 lines elided |
↑ open up ↑ |
300 301 ipmp6_created="$ipmp6_created $1"
301 302 else
302 303 ipmp6_failed="$ipmp6_failed $1"
303 304 fi
304 305 shift
305 306 done
306 307 [ -n "$ipmp6_failed" ] && warn_failed_ifs "create IPv6 IPMP" \
307 308 "$ipmp6_failed"
308 309 fi
309 310
311 +#
312 +# Upgrade ipadm.conf.
313 +#
314 +if /usr/bin/grep -q _family /etc/ipadm/ipadm.conf; then
315 + oldifs=$(/usr/bin/sed -En \
316 + 's/^_ifname=([a-z0-9]+);_family=[0-9]+;$/\1/p' \
317 + /etc/ipadm/ipadm.conf | /usr/bin/sort -u)
318 + /usr/bin/sed -i '/_family/d' /etc/ipadm/ipadm.conf
319 + for oldif in $oldifs; do
320 + /usr/bin/printf \
321 + "_ifname=%s;_ifclass=0;_families=2,26;\n" \
322 + $oldif >> /etc/ipadm/ipadm.conf
323 + done
324 +fi
325 +
310 326 #
311 327 # Finally configure interfaces set up with ipadm. Any /etc/hostname*.intf
312 328 # files take precedence over ipadm defined configurations except when
313 329 # we are in a non-global zone and Layer-3 protection of IP addresses is
314 330 # enforced on the interface by the global zone.
315 331 #
316 332 for showif_output in `/sbin/ipadm show-if -p -o ifname,state,current`; do
317 333 intf=`echo $showif_output | /usr/bin/cut -f1 -d:`
318 334 state=`echo $showif_output | /usr/bin/cut -f2 -d:`
319 335 current=`echo $showif_output | /usr/bin/cut -f3 -d:`
320 336 if [[ "$state" != "disabled" && $current != *Z* ]]; then
321 337 #
322 338 # skip if not a persistent interface, or if it should get IP
323 339 # configuration from the global zone ('Z' flag is set)
324 340 #
325 341 continue;
326 342 elif is_iptun $intf; then
327 343 # skip IP tunnel interfaces plumbed by net-iptun
328 344 continue;
329 345 elif [ -f /etc/hostname.$intf ] || [ -f /etc/hostname6.$intf ]; then
330 346 if [[ $current != *Z* ]]; then
331 347 echo "found /etc/hostname.$intf "\
332 348 "or /etc/hostname6.$intf, "\
333 349 "ignoring ipadm configuration" > /dev/msglog
334 350 continue;
335 351 else
336 352 echo "Ignoring /etc/hostname*.$intf" > /dev/msglog
337 353 /sbin/ifconfig $intf unplumb > /dev/null 2>&1
338 354 /sbin/ifconfig $intf inet6 unplumb > /dev/null 2>&1
339 355 fi
340 356 fi
341 357
342 358 # Enable the interface managed by ipadm
343 359 /sbin/ipadm enable-if -t $intf
344 360 done
345 361
346 362 #
347 363 # Process the /etc/hostname[6].* files for IPMP interfaces. Processing these
348 364 # before non-IPMP interfaces avoids accidental implicit IPMP group creation.
349 365 #
350 366 [ -n "$ipmp_created" ] && if_configure inet "IPMP" $ipmp_created
351 367 [ -n "$ipmp6_created" ] && if_configure inet6 "IPMP" $ipmp6_created
352 368
353 369 #
354 370 # Process the /etc/hostname[6].* files for non-IPMP interfaces.
355 371 #
356 372 [ -n "$inet_plumbed" ] && if_configure inet "" $inet_plumbed
357 373 [ -n "$inet6_plumbed" ] && if_configure inet6 "" $inet6_plumbed
358 374
359 375 #
360 376 # For the IPv4 and IPv6 interfaces that failed to plumb, find (or create)
361 377 # IPMP meta-interfaces to host their data addresses.
362 378 #
363 379 [ -n "$inet_failed" ] && move_addresses inet
364 380 [ -n "$inet6_failed" ] && move_addresses inet6
365 381
366 382 # Run DHCP if requested. Skip boot-configured interface.
367 383 interface_names="`echo /etc/dhcp.*[0-9] 2>/dev/null`"
368 384 if [ "$interface_names" != '/etc/dhcp.*[0-9]' ]; then
369 385 #
370 386 # First find the primary interface. Default to the first
371 387 # interface if not specified. First primary interface found
372 388 # "wins". Use care not to "reconfigure" a net-booted interface
373 389 # configured using DHCP. Run through the list of interfaces
374 390 # again, this time trying DHCP.
375 391 #
376 392 i4d_fail=
377 393 firstif=
378 394 primary=
379 395 ORIGIFS="$IFS"
380 396 IFS="${IFS}."
381 397 set -- $interface_names
382 398
383 399 while [ $# -ge 2 ]; do
384 400 shift
385 401 [ -z "$firstif" ] && firstif=$1
386 402
387 403 for i in `shcat /etc/dhcp\.$1`; do
388 404 if [ "$i" = primary ]; then
389 405 primary=$1
390 406 break
391 407 fi
392 408 done
393 409
394 410 [ -n "$primary" ] && break
395 411 shift
396 412 done
397 413
398 414 [ -z "$primary" ] && primary="$firstif"
399 415 cmdline=`shcat /etc/dhcp\.${primary}`
400 416
401 417 if [ "$_INIT_NET_IF" != "$primary" ]; then
402 418 echo "starting DHCP on primary interface $primary"
403 419 /sbin/ifconfig $primary auto-dhcp primary $cmdline
404 420 # Exit code 4 means ifconfig timed out waiting for dhcpagent
405 421 [ $? != 0 ] && [ $? != 4 ] && i4d_fail="$i4d_fail $primary"
406 422 fi
407 423
408 424 set -- $interface_names
409 425
410 426 while [ $# -ge 2 ]; do
411 427 shift
412 428 cmdline=`shcat /etc/dhcp\.$1`
413 429 if [ "$1" != "$primary" -a \
414 430 "$1" != "$_INIT_NET_IF" ]; then
415 431 echo "starting DHCP on interface $1"
416 432 /sbin/ifconfig $1 dhcp start wait 0 $cmdline
417 433 # Exit code can't be timeout when wait is 0
418 434 [ $? != 0 ] && i4d_fail="$i4d_fail $1"
419 435 fi
420 436 shift
421 437 done
422 438 IFS="$ORIGIFS"
423 439 unset ORIGIFS
424 440 [ -n "$i4d_fail" ] && warn_failed_ifs "configure IPv4 DHCP" "$i4d_fail"
425 441 fi
426 442
427 443 # In order to avoid bringing up the interfaces that have
428 444 # intentionally been left down, perform RARP only if the system
429 445 # has no configured hostname in /etc/nodename
430 446 hostname="`shcat /etc/nodename 2>/dev/null`"
431 447 if [ "$_INIT_NET_STRATEGY" = "rarp" -o -z "$hostname" ]; then
432 448 /sbin/ifconfig -adD4 auto-revarp netmask + broadcast + up
433 449 fi
434 450
435 451 #
436 452 # If the /etc/defaultrouter file exists, process it now so that the next
437 453 # stage of booting will have access to NFS.
438 454 #
439 455 if [ -f /etc/defaultrouter ]; then
440 456 while read router rubbish; do
441 457 case "$router" in
442 458 '#'* | '') ;; # Ignore comments, empty lines
443 459 *) /sbin/route -n add default -gateway $router ;;
444 460 esac
445 461 done </etc/defaultrouter
446 462 fi
447 463
448 464 #
449 465 # If we get here and were not asked to plumb any IPv4 interfaces, look
450 466 # for boot properties that direct us.
451 467 #
452 468 # - The "network-interface" property is required and indicates the
453 469 # interface name.
454 470 # - The "xpv-hcp" property, if present, is used by the hypervisor
455 471 # tools to indicate how the specified interface should be configured.
456 472 # Permitted values are "dhcp" and "off", where "off" indicates static
457 473 # IP configuration.
458 474 #
459 475 # In the case where "xpv-hcp" is set to "dhcp", no further properties
460 476 # are required or examined.
461 477 #
462 478 # In the case where "xpv-hcp" is not present or set to "off", the
463 479 # "host-ip" and "subnet-mask" properties are used to configure
464 480 # the specified interface. The "router-ip" property, if present,
465 481 # is used to add a default route.
466 482 #
467 483 nic="`/sbin/devprop network-interface`"
468 484 if smf_is_globalzone && [ -z "$inet_list" ] && [ -n "$nic" ]; then
469 485 hcp="`/sbin/devprop xpv-hcp`"
470 486 case "$hcp" in
471 487 "dhcp")
472 488 /sbin/ifconfig $nic plumb 2>/dev/null
473 489 [ -n "`/sbin/ifconfig $nic 2>/dev/null`" ] && (
474 490 # The interface is successfully plumbed, so
475 491 # modify "inet_list" to force the exit code
476 492 # checks to work.
477 493 inet_list=$nic;
478 494 # Given that this is the only IPv4 interface,
479 495 # we assert that it is primary.
480 496 echo "starting DHCP on primary interface $primary";
481 497 /sbin/ifconfig $nic auto-dhcp primary;
482 498 # Exit code 4 means ifconfig timed out waiting
483 499 # for dhcpagent
484 500 [ $? != 0 ] && [ $? != 4 ] && \
485 501 i4d_fail="$i4d_fail $nic";
486 502 )
487 503 ;;
488 504
489 505 "off"|"")
490 506 /sbin/devprop host-ip subnet-mask router-ip | (
491 507 read ip;
492 508 read mask;
493 509 read router;
494 510 [ -n "$ip" ] && [ -n "$mask" ] && \
495 511 /sbin/ifconfig $nic plumb 2>/dev/null
496 512 [ -n "`/sbin/ifconfig $nic 2>/dev/null`" ] && (
497 513 # The interface is successfully
498 514 # plumbed, so modify "inet_list" to
499 515 # force the exit code checks to work.
500 516 inet_list=$nic;
501 517 /sbin/ifconfig $nic inet $ip \
502 518 netmask $mask broadcast + up 2>/dev/null;
503 519 [ -n "$router" ] && route add \
504 520 default $router 2>/dev/null;
505 521 )
506 522 )
507 523 ;;
508 524 esac
509 525 fi
510 526
511 527 #
512 528 # We tell smf this service is online if any of the following is true:
513 529 # - no interfaces were configured for plumbing and no DHCP failures
514 530 # - any non-loopback IPv4 interfaces are up and have a non-zero address
515 531 # - there are any DHCP interfaces started
516 532 # - any non-loopback IPv6 interfaces are up
517 533 #
518 534 # If we weren't asked to configure any interfaces, exit
519 535 if [ -z "$inet_list" ] && [ -z "$inet6_list" ]; then
520 536 # Config error if DHCP was attempted without plumbed interfaces
521 537 [ -n "$i4d_fail" ] && exit $SMF_EXIT_ERR_CONFIG
522 538 exit $SMF_EXIT_OK
523 539 fi
524 540
525 541 # Any non-loopback IPv4 interfaces with usable addresses up?
526 542 if [ -n "`/sbin/ifconfig -a4u`" ]; then
527 543 /sbin/ifconfig -a4u | while read intf addr rest; do
528 544 [ $intf = inet ] && [ $addr != 127.0.0.1 ] &&
529 545 [ $addr != 0.0.0.0 ] && exit $SMF_EXIT_OK
530 546 done && exit $SMF_EXIT_OK
531 547 fi
532 548
533 549 # Any DHCP interfaces started?
534 550 [ -n "`/sbin/ifconfig -a4 dhcp status 2>/dev/null`" ] && exit $SMF_EXIT_OK
535 551
536 552 # Any non-loopback IPv6 interfaces up?
537 553 if [ -n "`/sbin/ifconfig -au6`" ]; then
538 554 /sbin/ifconfig -au6 | while read intf addr rest; do
539 555 [ $intf = inet6 ] && [ $addr != ::1/128 ] && exit $SMF_EXIT_OK
540 556 done && exit $SMF_EXIT_OK
541 557 fi
542 558
543 559 # This service was supposed to configure something yet didn't. Exit
544 560 # with config error.
545 561 exit $SMF_EXIT_ERR_CONFIG
|
↓ open down ↓ |
226 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX