Attachment "scripts-for-review-2.tar" to
tech note [bae6999eb9]
added by
opus-ocx2
2026-10-29 01:50:10.
0000: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69 scripts-for-revi
0010: 65 77 2d 32 2f 00 00 00 00 00 00 00 00 00 00 00 ew-2/...........
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0060: 00 00 00 00 30 30 30 30 37 35 35 00 30 30 30 30 ....0000755.0000
0070: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 000.0000000.0000
0080: 30 30 30 30 30 30 30 00 31 35 32 37 30 35 32 33 0000000.15270523
0090: 31 32 32 00 30 31 33 34 36 34 00 20 35 00 00 00 122.013464. 5...
00a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0100: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00 .ustar .root...
0110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0120: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00 .........root...
0130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0200: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69 scripts-for-revi
0210: 65 77 2d 32 2f 73 74 61 72 74 2d 6f 63 78 2e 63 ew-2/start-ocx.c
0220: 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r...............
0230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0260: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30 ....0000644.0000
0270: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 000.0000000.0000
0280: 30 30 32 33 34 35 30 00 31 35 32 37 30 35 32 33 0023450.15270523
0290: 31 32 32 00 30 31 35 37 34 32 00 20 30 00 00 00 122.015742. 0...
02a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
02b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
02c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
02d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
02e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
02f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0300: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00 .ustar .root...
0310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0320: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00 .........root...
0330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
03a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
03b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
03c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
03d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
03e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
03f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0400: 23 20 41 67 65 6e 74 73 3a 20 64 6f 20 6e 6f 74 # Agents: do not
0410: 20 63 68 61 6e 67 65 20 74 68 69 73 20 66 69 6c change this fil
0420: 65 20 6f 6e 20 61 6e 79 20 62 72 61 6e 63 68 20 e on any branch
0430: 77 69 74 68 6f 75 74 20 74 68 65 20 6d 61 69 6e without the main
0440: 74 61 69 6e 65 72 27 73 20 65 78 70 6c 69 63 69 tainer's explici
0450: 74 20 61 70 70 72 6f 76 61 6c 20 6f 6e 20 74 68 t approval on th
0460: 65 20 72 65 63 6f 72 64 2e 0a 23 20 53 65 6c 66 e record..# Self
0470: 2d 63 6f 6e 74 61 69 6e 65 64 20 68 6f 73 74 20 -contained host
0480: 6c 61 75 6e 63 68 65 72 3a 20 6f 6e 6c 79 20 74 launcher: only t
0490: 68 65 20 70 61 74 68 73 20 62 65 6c 6f 77 2c 20 he paths below,
04a0: 72 65 6c 65 61 73 65 20 6c 69 6e 6b 73 2c 20 2f release links, /
04b0: 70 72 6f 63 2f 73 65 6c 66 2f 66 64 2c 0a 23 20 proc/self/fd,.#
04c0: 65 78 65 63 75 74 61 62 6c 65 20 6c 6f 63 61 74 executable locat
04d0: 69 6f 6e 2c 20 65 78 69 73 74 69 6e 67 20 68 6f ion, existing ho
04e0: 6d 65 20 70 61 74 68 73 20 61 6e 64 20 46 6f 73 me paths and Fos
04f0: 73 69 6c 27 73 20 43 47 49 20 65 6e 76 69 72 6f sil's CGI enviro
0500: 6e 6d 65 6e 74 20 61 72 65 20 72 65 61 64 2e 0a nment are read..
0510: 23 20 42 75 69 6c 64 20 62 65 73 69 64 65 20 72 # Build beside r
0520: 65 63 65 69 70 74 73 20 77 69 74 68 20 60 73 68 eceipts with `sh
0530: 61 72 64 73 20 62 75 69 6c 64 20 73 74 61 72 74 ards build start
0540: 2d 6f 63 78 20 72 65 63 65 69 70 74 73 60 20 69 -ocx receipts` i
0550: 6e 20 61 20 63 68 65 63 6b 6f 75 74 2e 0a 72 65 n a checkout..re
0560: 71 75 69 72 65 20 22 6a 73 6f 6e 22 0a 72 65 71 quire "json".req
0570: 75 69 72 65 20 22 73 6f 63 6b 65 74 22 0a 0a 6c uire "socket"..l
0580: 69 62 20 4c 69 62 43 0a 20 20 53 4f 5f 53 4e 44 ib LibC. SO_SND
0590: 54 49 4d 45 4f 20 3d 20 32 31 0a 0a 20 20 66 75 TIMEO = 21.. fu
05a0: 6e 20 65 78 65 63 76 28 70 61 74 68 20 3a 20 43 n execv(path : C
05b0: 68 61 72 2a 2c 20 61 72 67 76 20 3a 20 43 68 61 har*, argv : Cha
05c0: 72 2a 2a 29 20 3a 20 49 6e 74 0a 65 6e 64 0a 0a r**) : Int.end..
05d0: 6d 6f 64 75 6c 65 20 53 74 61 72 74 4f 43 58 0a module StartOCX.
05e0: 20 20 4c 49 56 45 5f 48 4f 53 54 20 20 20 20 20 LIVE_HOST
05f0: 20 20 20 20 3d 20 22 69 70 2d 31 37 32 2d 33 31 = "ip-172-31
0600: 2d 31 37 2d 31 37 37 2e 75 73 2d 77 65 73 74 2d -17-177.us-west-
0610: 32 2e 63 6f 6d 70 75 74 65 2e 69 6e 74 65 72 6e 2.compute.intern
0620: 61 6c 22 0a 20 20 4c 49 56 45 5f 52 45 50 4f 53 al". LIVE_REPOS
0630: 49 54 4f 52 49 45 53 20 3d 20 22 2f 76 61 72 2f ITORIES = "/var/
0640: 6c 69 62 2f 66 6f 73 73 69 6c 22 0a 20 20 4c 49 lib/fossil". LI
0650: 56 45 5f 53 4f 43 4b 45 54 5f 44 49 52 20 20 20 VE_SOCKET_DIR
0660: 3d 20 22 2f 72 75 6e 2f 6f 63 78 22 0a 20 20 4c = "/run/ocx". L
0670: 49 56 45 5f 52 45 4c 45 41 53 45 20 20 20 20 20 IVE_RELEASE
0680: 20 3d 20 22 2f 76 61 72 2f 6c 69 62 2f 6f 63 78 = "/var/lib/ocx
0690: 2f 72 65 6c 65 61 73 65 32 22 0a 20 20 4c 49 56 /release2". LIV
06a0: 45 5f 44 41 45 4d 4f 4e 20 20 20 20 20 20 20 3d E_DAEMON =
06b0: 20 22 2f 76 61 72 2f 6c 69 62 2f 6f 63 78 2f 61 "/var/lib/ocx/a
06c0: 67 65 6e 74 73 2d 64 61 65 6d 6f 6e 22 0a 20 20 gents-daemon".
06d0: 23 20 74 68 65 20 64 61 65 6d 6f 6e 27 73 20 61 # the daemon's a
06e0: 63 63 6f 75 6e 74 20 6b 65 65 70 73 20 69 74 73 ccount keeps its
06f0: 20 68 6f 6d 65 73 20 68 65 72 65 3b 20 69 6e 73 homes here; ins
0700: 69 64 65 20 69 74 73 20 6e 61 6d 65 73 70 61 63 ide its namespac
0710: 65 20 74 68 65 79 20 61 72 65 20 61 74 0a 20 20 e they are at.
0720: 23 20 2f 72 6f 6f 74 2c 20 77 68 65 72 65 20 72 # /root, where r
0730: 75 6e 73 27 20 70 61 74 74 65 72 6e 73 20 61 6e uns' patterns an
0740: 64 20 72 2d 73 65 6e 64 65 72 73 2e 6a 73 6f 6e d r-senders.json
0750: 20 6e 61 6d 65 20 74 68 65 6d 0a 20 20 4c 49 56 name them. LIV
0760: 45 5f 48 4f 4d 45 5f 53 4f 55 52 43 45 20 3d 20 E_HOME_SOURCE =
0770: 22 2f 68 6f 6d 65 2f 6f 63 78 22 0a 20 20 4c 49 "/home/ocx". LI
0780: 56 45 5f 48 4f 4d 45 20 20 20 20 20 20 20 20 3d VE_HOME =
0790: 20 22 2f 72 6f 6f 74 22 0a 20 20 43 47 49 5f 46 "/root". CGI_F
07a0: 49 45 4c 44 53 20 20 20 20 20 20 20 3d 20 25 77 IELDS = %w
07b0: 28 52 45 51 55 45 53 54 5f 4d 45 54 48 4f 44 20 (REQUEST_METHOD
07c0: 51 55 45 52 59 5f 53 54 52 49 4e 47 20 50 41 54 QUERY_STRING PAT
07d0: 48 5f 49 4e 46 4f 20 43 4f 4e 54 45 4e 54 5f 4c H_INFO CONTENT_L
07e0: 45 4e 47 54 48 20 43 4f 4e 54 45 4e 54 5f 54 59 ENGTH CONTENT_TY
07f0: 50 45 20 48 54 54 50 53 20 48 54 54 50 5f 48 4f PE HTTPS HTTP_HO
0800: 53 54 20 48 54 54 50 5f 52 45 46 45 52 45 52 20 ST HTTP_REFERER
0810: 46 4f 53 53 49 4c 5f 55 53 45 52 20 46 4f 53 53 FOSSIL_USER FOSS
0820: 49 4c 5f 43 41 50 41 42 49 4c 49 54 49 45 53 20 IL_CAPABILITIES
0830: 46 4f 53 53 49 4c 5f 55 52 49 29 0a 20 20 4c 49 FOSSIL_URI). LI
0840: 42 52 41 52 49 45 53 20 20 20 20 20 20 20 20 3d BRARIES =
0850: 20 25 77 28 2f 6c 69 62 20 2f 6c 69 62 36 34 29 %w(/lib /lib64)
0860: 0a 20 20 44 41 45 4d 4f 4e 5f 53 59 53 54 45 4d . DAEMON_SYSTEM
0870: 20 20 20 20 3d 20 25 77 28 2f 6c 69 62 20 2f 6c = %w(/lib /l
0880: 69 62 36 34 20 2f 62 69 6e 20 2f 73 62 69 6e 20 ib64 /bin /sbin
0890: 2f 65 74 63 2f 73 73 6c 20 2f 65 74 63 2f 70 6b /etc/ssl /etc/pk
08a0: 69 20 2f 65 74 63 2f 72 65 73 6f 6c 76 2e 63 6f i /etc/resolv.co
08b0: 6e 66 20 2f 65 74 63 2f 68 6f 73 74 73 20 2f 65 nf /etc/hosts /e
08c0: 74 63 2f 6e 73 73 77 69 74 63 68 2e 63 6f 6e 66 tc/nsswitch.conf
08d0: 20 2f 65 74 63 2f 70 61 73 73 77 64 20 2f 65 74 /etc/passwd /et
08e0: 63 2f 67 72 6f 75 70 29 0a 0a 20 20 23 20 65 61 c/group).. # ea
08f0: 63 68 20 68 6f 6d 65 3a 20 77 68 65 72 65 20 74 ch home: where t
0900: 68 65 20 61 63 63 6f 75 6e 74 20 6b 65 65 70 73 he account keeps
0910: 20 69 74 2c 20 77 68 65 72 65 20 74 68 65 20 64 it, where the d
0920: 61 65 6d 6f 6e 20 73 65 65 73 20 69 74 2c 20 61 aemon sees it, a
0930: 6e 64 20 77 68 65 74 68 65 72 20 69 74 20 6d 61 nd whether it ma
0940: 79 20 77 72 69 74 65 20 74 68 65 72 65 0a 20 20 y write there.
0950: 72 65 63 6f 72 64 20 50 61 74 68 73 2c 20 72 65 record Paths, re
0960: 70 6f 73 69 74 6f 72 69 65 73 20 3a 20 53 74 72 positories : Str
0970: 69 6e 67 2c 20 73 6f 63 6b 65 74 5f 64 69 72 20 ing, socket_dir
0980: 3a 20 53 74 72 69 6e 67 2c 20 72 65 6c 65 61 73 : String, releas
0990: 65 20 3a 20 53 74 72 69 6e 67 2c 20 64 61 65 6d e : String, daem
09a0: 6f 6e 20 3a 20 53 74 72 69 6e 67 2c 20 68 6f 6d on : String, hom
09b0: 65 20 3a 20 53 74 72 69 6e 67 2c 20 68 6f 6d 65 e : String, home
09c0: 73 20 3a 20 41 72 72 61 79 28 54 75 70 6c 65 28 s : Array(Tuple(
09d0: 53 74 72 69 6e 67 2c 20 53 74 72 69 6e 67 2c 20 String, String,
09e0: 42 6f 6f 6c 29 29 20 64 6f 0a 20 20 20 20 64 65 Bool)) do. de
09f0: 66 20 73 6f 63 6b 65 74 20 3a 20 53 74 72 69 6e f socket : Strin
0a00: 67 0a 20 20 20 20 20 20 46 69 6c 65 2e 6a 6f 69 g. File.joi
0a10: 6e 28 73 6f 63 6b 65 74 5f 64 69 72 2c 20 22 72 n(socket_dir, "r
0a20: 65 63 65 69 70 74 73 2e 73 6f 63 6b 22 29 0a 20 eceipts.sock").
0a30: 20 20 20 65 6e 64 0a 20 20 65 6e 64 0a 0a 20 20 end. end..
0a40: 64 65 66 20 73 65 6c 66 2e 70 61 74 68 73 28 73 def self.paths(s
0a50: 63 72 61 74 63 68 20 3a 20 53 74 72 69 6e 67 3f cratch : String?
0a60: 2c 20 68 6f 73 74 20 3a 20 53 74 72 69 6e 67 29 , host : String)
0a70: 20 3a 20 50 61 74 68 73 0a 20 20 20 20 69 66 20 : Paths. if
0a80: 73 63 72 61 74 63 68 0a 20 20 20 20 20 20 72 61 scratch. ra
0a90: 69 73 65 20 41 72 67 75 6d 65 6e 74 45 72 72 6f ise ArgumentErro
0aa0: 72 2e 6e 65 77 28 22 73 63 72 61 74 63 68 20 64 r.new("scratch d
0ab0: 69 72 65 63 74 6f 72 79 20 6d 75 73 74 20 62 65 irectory must be
0ac0: 20 61 62 73 6f 6c 75 74 65 22 29 20 75 6e 6c 65 absolute") unle
0ad0: 73 73 20 73 63 72 61 74 63 68 2e 73 74 61 72 74 ss scratch.start
0ae0: 73 5f 77 69 74 68 3f 28 22 2f 22 29 0a 20 20 20 s_with?("/").
0af0: 20 20 20 72 6f 6f 74 20 3d 20 46 69 6c 65 2e 65 root = File.e
0b00: 78 70 61 6e 64 5f 70 61 74 68 28 73 63 72 61 74 xpand_path(scrat
0b10: 63 68 29 0a 20 20 20 20 20 20 68 6f 6d 65 20 3d ch). home =
0b20: 20 46 69 6c 65 2e 6a 6f 69 6e 28 72 6f 6f 74 2c File.join(root,
0b30: 20 22 68 6f 6d 65 22 29 0a 20 20 20 20 20 20 50 "home"). P
0b40: 61 74 68 73 2e 6e 65 77 28 46 69 6c 65 2e 6a 6f aths.new(File.jo
0b50: 69 6e 28 72 6f 6f 74 2c 20 22 66 6f 73 73 69 6c in(root, "fossil
0b60: 22 29 2c 20 46 69 6c 65 2e 6a 6f 69 6e 28 72 6f "), File.join(ro
0b70: 6f 74 2c 20 22 72 75 6e 22 29 2c 20 46 69 6c 65 ot, "run"), File
0b80: 2e 6a 6f 69 6e 28 72 6f 6f 74 2c 20 22 72 65 6c .join(root, "rel
0b90: 65 61 73 65 22 29 2c 20 46 69 6c 65 2e 6a 6f 69 ease"), File.joi
0ba0: 6e 28 72 6f 6f 74 2c 20 22 64 61 65 6d 6f 6e 22 n(root, "daemon"
0bb0: 29 2c 20 68 6f 6d 65 2c 20 5b 7b 46 69 6c 65 2e ), home, [{File.
0bc0: 6a 6f 69 6e 28 68 6f 6d 65 2c 20 22 2e 63 6f 64 join(home, ".cod
0bd0: 65 78 22 29 2c 20 46 69 6c 65 2e 6a 6f 69 6e 28 ex"), File.join(
0be0: 68 6f 6d 65 2c 20 22 2e 63 6f 64 65 78 22 29 2c home, ".codex"),
0bf0: 20 74 72 75 65 7d 5d 29 0a 20 20 20 20 65 6c 73 true}]). els
0c00: 69 66 20 68 6f 73 74 20 3d 3d 20 4c 49 56 45 5f if host == LIVE_
0c10: 48 4f 53 54 0a 20 20 20 20 20 20 50 61 74 68 73 HOST. Paths
0c20: 2e 6e 65 77 28 4c 49 56 45 5f 52 45 50 4f 53 49 .new(LIVE_REPOSI
0c30: 54 4f 52 49 45 53 2c 20 4c 49 56 45 5f 53 4f 43 TORIES, LIVE_SOC
0c40: 4b 45 54 5f 44 49 52 2c 20 4c 49 56 45 5f 52 45 KET_DIR, LIVE_RE
0c50: 4c 45 41 53 45 2c 20 4c 49 56 45 5f 44 41 45 4d LEASE, LIVE_DAEM
0c60: 4f 4e 2c 20 4c 49 56 45 5f 48 4f 4d 45 2c 0a 20 ON, LIVE_HOME,.
0c70: 20 20 20 20 20 20 20 5b 22 2e 63 6f 64 65 78 22 [".codex"
0c80: 2c 20 22 2e 63 6c 61 75 64 65 22 2c 20 22 2e 63 , ".claude", ".c
0c90: 6c 61 75 64 65 2e 6a 73 6f 6e 22 5d 2e 6d 61 70 laude.json"].map
0ca0: 20 7b 20 7c 68 7c 20 7b 46 69 6c 65 2e 6a 6f 69 { |h| {File.joi
0cb0: 6e 28 4c 49 56 45 5f 48 4f 4d 45 5f 53 4f 55 52 n(LIVE_HOME_SOUR
0cc0: 43 45 2c 20 68 29 2c 20 46 69 6c 65 2e 6a 6f 69 CE, h), File.joi
0cd0: 6e 28 4c 49 56 45 5f 48 4f 4d 45 2c 20 68 29 2c n(LIVE_HOME, h),
0ce0: 20 74 72 75 65 7d 20 7d 20 2b 0a 20 20 20 20 20 true} } +.
0cf0: 20 20 20 5b 7b 46 69 6c 65 2e 6a 6f 69 6e 28 4c [{File.join(L
0d00: 49 56 45 5f 48 4f 4d 45 5f 53 4f 55 52 43 45 2c IVE_HOME_SOURCE,
0d10: 20 22 2e 6c 6f 63 61 6c 22 29 2c 20 46 69 6c 65 ".local"), File
0d20: 2e 6a 6f 69 6e 28 4c 49 56 45 5f 48 4f 4d 45 2c .join(LIVE_HOME,
0d30: 20 22 2e 6c 6f 63 61 6c 22 29 2c 20 66 61 6c 73 ".local"), fals
0d40: 65 7d 5d 29 0a 20 20 20 20 65 6c 73 65 0a 20 20 e}]). else.
0d50: 20 20 20 20 72 61 69 73 65 20 41 72 67 75 6d 65 raise Argume
0d60: 6e 74 45 72 72 6f 72 2e 6e 65 77 28 22 75 6e 6b ntError.new("unk
0d70: 6e 6f 77 6e 20 68 6f 73 74 3b 20 75 73 65 20 2d nown host; use -
0d80: 2d 73 63 72 61 74 63 68 20 44 49 52 22 29 0a 20 -scratch DIR").
0d90: 20 20 20 65 6e 64 0a 20 20 65 6e 64 0a 0a 20 20 end. end..
0da0: 23 20 54 68 65 73 65 20 74 68 72 65 65 20 62 75 # These three bu
0db0: 69 6c 64 65 72 73 20 72 65 61 64 20 6e 6f 74 68 ilders read noth
0dc0: 69 6e 67 3a 20 74 68 65 69 72 20 66 75 6c 6c 20 ing: their full
0dd0: 61 72 67 75 6d 65 6e 74 20 6c 69 73 74 73 20 63 argument lists c
0de0: 61 6e 20 62 65 20 69 6e 73 70 65 63 74 65 64 2e an be inspected.
0df0: 0a 20 20 64 65 66 20 73 65 6c 66 2e 63 67 69 5f . def self.cgi_
0e00: 61 72 67 73 28 72 65 6c 65 61 73 65 20 3a 20 53 args(release : S
0e10: 74 72 69 6e 67 2c 20 72 65 70 6f 73 69 74 6f 72 tring, repositor
0e20: 79 20 3a 20 53 74 72 69 6e 67 2c 20 65 6e 76 69 y : String, envi
0e30: 72 6f 6e 6d 65 6e 74 20 3a 20 48 61 73 68 28 53 ronment : Hash(S
0e40: 74 72 69 6e 67 2c 20 53 74 72 69 6e 67 29 29 20 tring, String))
0e50: 3a 20 41 72 72 61 79 28 53 74 72 69 6e 67 29 0a : Array(String).
0e60: 20 20 20 20 61 72 67 73 20 3d 20 25 77 28 2d 2d args = %w(--
0e70: 75 6e 73 68 61 72 65 2d 61 6c 6c 20 2d 2d 64 69 unshare-all --di
0e80: 65 2d 77 69 74 68 2d 70 61 72 65 6e 74 20 2d 2d e-with-parent --
0e90: 6e 65 77 2d 73 65 73 73 69 6f 6e 20 2d 2d 63 61 new-session --ca
0ea0: 70 2d 64 72 6f 70 20 41 4c 4c 20 2d 2d 63 6c 65 p-drop ALL --cle
0eb0: 61 72 65 6e 76 20 2d 2d 72 6f 2d 62 69 6e 64 20 arenv --ro-bind
0ec0: 2f 75 73 72 20 2f 75 73 72 29 0a 20 20 20 20 28 /usr /usr). (
0ed0: 4c 49 42 52 41 52 49 45 53 20 2b 20 5b 22 2f 62 LIBRARIES + ["/b
0ee0: 69 6e 22 2c 20 22 2f 65 74 63 2f 70 61 73 73 77 in", "/etc/passw
0ef0: 64 22 5d 29 2e 65 61 63 68 20 7b 20 7c 70 61 74 d"]).each { |pat
0f00: 68 7c 20 61 72 67 73 2e 63 6f 6e 63 61 74 28 5b h| args.concat([
0f10: 22 2d 2d 72 6f 2d 62 69 6e 64 2d 74 72 79 22 2c "--ro-bind-try",
0f20: 20 70 61 74 68 2c 20 70 61 74 68 5d 29 20 7d 0a path, path]) }.
0f30: 20 20 20 20 61 72 67 73 2e 63 6f 6e 63 61 74 28 args.concat(
0f40: 5b 22 2d 2d 70 72 6f 63 22 2c 20 22 2f 70 72 6f ["--proc", "/pro
0f50: 63 22 2c 20 22 2d 2d 64 65 76 22 2c 20 22 2f 64 c", "--dev", "/d
0f60: 65 76 22 2c 20 22 2d 2d 74 6d 70 66 73 22 2c 20 ev", "--tmpfs",
0f70: 22 2f 74 6d 70 22 2c 20 22 2d 2d 72 6f 2d 62 69 "/tmp", "--ro-bi
0f80: 6e 64 22 2c 20 46 69 6c 65 2e 6a 6f 69 6e 28 72 nd", File.join(r
0f90: 65 6c 65 61 73 65 2c 20 22 6f 63 78 32 22 29 2c elease, "ocx2"),
0fa0: 20 22 2f 72 65 6c 65 61 73 65 2f 6f 63 78 32 2d "/release/ocx2-
0fb0: 63 67 69 22 5d 29 0a 20 20 20 20 43 47 49 5f 46 cgi"]). CGI_F
0fc0: 49 45 4c 44 53 2e 65 61 63 68 20 64 6f 20 7c 6b IELDS.each do |k
0fd0: 65 79 7c 0a 20 20 20 20 20 20 69 66 20 76 61 6c ey|. if val
0fe0: 75 65 20 3d 20 65 6e 76 69 72 6f 6e 6d 65 6e 74 ue = environment
0ff0: 5b 6b 65 79 5d 3f 0a 20 20 20 20 20 20 20 20 61 [key]?. a
1000: 72 67 73 2e 63 6f 6e 63 61 74 28 5b 22 2d 2d 73 rgs.concat(["--s
1010: 65 74 65 6e 76 22 2c 20 6b 65 79 2c 20 76 61 6c etenv", key, val
1020: 75 65 5d 29 0a 20 20 20 20 20 20 65 6e 64 0a 20 ue]). end.
1030: 20 20 20 65 6e 64 0a 20 20 20 20 61 72 67 73 2e end. args.
1040: 63 6f 6e 63 61 74 28 5b 22 2d 2d 73 65 74 65 6e concat(["--seten
1050: 76 22 2c 20 22 46 4f 53 53 49 4c 5f 52 45 50 4f v", "FOSSIL_REPO
1060: 53 49 54 4f 52 59 22 2c 20 72 65 70 6f 73 69 74 SITORY", reposit
1070: 6f 72 79 2c 20 22 2d 2d 73 65 74 65 6e 76 22 2c ory, "--setenv",
1080: 20 22 50 41 54 48 22 2c 20 22 2f 75 73 72 2f 62 "PATH", "/usr/b
1090: 69 6e 22 2c 20 22 2f 72 65 6c 65 61 73 65 2f 6f in", "/release/o
10a0: 63 78 32 2d 63 67 69 22 2c 20 22 2d 2d 72 65 63 cx2-cgi", "--rec
10b0: 65 69 70 74 2d 66 64 22 2c 20 22 33 22 5d 29 0a eipt-fd", "3"]).
10c0: 20 20 65 6e 64 0a 0a 20 20 64 65 66 20 73 65 6c end.. def sel
10d0: 66 2e 72 65 63 65 69 70 74 73 5f 61 72 67 73 28 f.receipts_args(
10e0: 70 61 74 68 73 20 3a 20 50 61 74 68 73 2c 20 65 paths : Paths, e
10f0: 78 65 63 75 74 61 62 6c 65 5f 64 69 72 20 3a 20 xecutable_dir :
1100: 53 74 72 69 6e 67 29 20 3a 20 41 72 72 61 79 28 String) : Array(
1110: 53 74 72 69 6e 67 29 0a 20 20 20 20 61 72 67 73 String). args
1120: 20 3d 20 25 77 28 2d 2d 75 6e 73 68 61 72 65 2d = %w(--unshare-
1130: 61 6c 6c 20 2d 2d 64 69 65 2d 77 69 74 68 2d 70 all --die-with-p
1140: 61 72 65 6e 74 20 2d 2d 6e 65 77 2d 73 65 73 73 arent --new-sess
1150: 69 6f 6e 20 2d 2d 63 61 70 2d 64 72 6f 70 20 41 ion --cap-drop A
1160: 4c 4c 20 2d 2d 63 6c 65 61 72 65 6e 76 20 2d 2d LL --clearenv --
1170: 72 6f 2d 62 69 6e 64 20 2f 75 73 72 20 2f 75 73 ro-bind /usr /us
1180: 72 29 0a 20 20 20 20 4c 49 42 52 41 52 49 45 53 r). LIBRARIES
1190: 2e 65 61 63 68 20 7b 20 7c 70 61 74 68 7c 20 61 .each { |path| a
11a0: 72 67 73 2e 63 6f 6e 63 61 74 28 5b 22 2d 2d 72 rgs.concat(["--r
11b0: 6f 2d 62 69 6e 64 2d 74 72 79 22 2c 20 70 61 74 o-bind-try", pat
11c0: 68 2c 20 70 61 74 68 5d 29 20 7d 0a 20 20 20 20 h, path]) }.
11d0: 61 72 67 73 2e 63 6f 6e 63 61 74 28 5b 22 2d 2d args.concat(["--
11e0: 64 65 76 22 2c 20 22 2f 64 65 76 22 2c 20 22 2d dev", "/dev", "-
11f0: 2d 74 6d 70 66 73 22 2c 20 22 2f 74 6d 70 22 2c -tmpfs", "/tmp",
1200: 20 22 2d 2d 72 6f 2d 62 69 6e 64 22 2c 20 70 61 "--ro-bind", pa
1210: 74 68 73 2e 72 65 70 6f 73 69 74 6f 72 69 65 73 ths.repositories
1220: 2c 20 70 61 74 68 73 2e 72 65 70 6f 73 69 74 6f , paths.reposito
1230: 72 69 65 73 2c 0a 20 20 20 20 20 20 20 20 20 20 ries,.
1240: 20 20 20 20 20 20 20 22 2d 2d 62 69 6e 64 22 2c "--bind",
1250: 20 70 61 74 68 73 2e 73 6f 63 6b 65 74 5f 64 69 paths.socket_di
1260: 72 2c 20 70 61 74 68 73 2e 73 6f 63 6b 65 74 5f r, paths.socket_
1270: 64 69 72 2c 20 22 2d 2d 72 6f 2d 62 69 6e 64 22 dir, "--ro-bind"
1280: 2c 20 46 69 6c 65 2e 6a 6f 69 6e 28 65 78 65 63 , File.join(exec
1290: 75 74 61 62 6c 65 5f 64 69 72 2c 20 22 72 65 63 utable_dir, "rec
12a0: 65 69 70 74 73 22 29 2c 20 22 2f 72 65 63 65 69 eipts"), "/recei
12b0: 70 74 73 22 2c 0a 20 20 20 20 20 20 20 20 20 20 pts",.
12c0: 20 20 20 20 20 20 20 22 2f 72 65 63 65 69 70 74 "/receipt
12d0: 73 22 2c 20 70 61 74 68 73 2e 73 6f 63 6b 65 74 s", paths.socket
12e0: 2c 20 70 61 74 68 73 2e 72 65 70 6f 73 69 74 6f , paths.reposito
12f0: 72 69 65 73 5d 29 0a 20 20 65 6e 64 0a 0a 20 20 ries]). end..
1300: 64 65 66 20 73 65 6c 66 2e 64 61 65 6d 6f 6e 5f def self.daemon_
1310: 61 72 67 73 28 70 61 74 68 73 20 3a 20 50 61 74 args(paths : Pat
1320: 68 73 2c 20 68 6f 6d 65 73 20 3a 20 41 72 72 61 hs, homes : Arra
1330: 79 28 54 75 70 6c 65 28 53 74 72 69 6e 67 2c 20 y(Tuple(String,
1340: 53 74 72 69 6e 67 2c 20 42 6f 6f 6c 29 29 29 20 String, Bool)))
1350: 3a 20 41 72 72 61 79 28 53 74 72 69 6e 67 29 0a : Array(String).
1360: 20 20 20 20 61 72 67 73 20 3d 20 5b 22 2d 2d 75 args = ["--u
1370: 6e 73 68 61 72 65 2d 75 73 65 72 22 2c 20 22 2d nshare-user", "-
1380: 2d 75 6e 73 68 61 72 65 2d 69 70 63 22 2c 20 22 -unshare-ipc", "
1390: 2d 2d 75 6e 73 68 61 72 65 2d 75 74 73 22 2c 20 --unshare-uts",
13a0: 22 2d 2d 63 6c 65 61 72 65 6e 76 22 2c 20 22 2d "--clearenv", "-
13b0: 2d 73 65 74 65 6e 76 22 2c 20 22 48 4f 4d 45 22 -setenv", "HOME"
13c0: 2c 20 70 61 74 68 73 2e 68 6f 6d 65 2c 0a 20 20 , paths.home,.
13d0: 20 20 20 20 20 20 20 20 20 20 22 2d 2d 73 65 74 "--set
13e0: 65 6e 76 22 2c 20 22 50 41 54 48 22 2c 20 22 2f env", "PATH", "/
13f0: 75 73 72 2f 62 69 6e 3a 2f 62 69 6e 22 2c 20 22 usr/bin:/bin", "
1400: 2d 2d 72 6f 2d 62 69 6e 64 22 2c 20 22 2f 75 73 --ro-bind", "/us
1410: 72 22 2c 20 22 2f 75 73 72 22 5d 0a 20 20 20 20 r", "/usr"].
1420: 44 41 45 4d 4f 4e 5f 53 59 53 54 45 4d 2e 65 61 DAEMON_SYSTEM.ea
1430: 63 68 20 7b 20 7c 70 61 74 68 7c 20 61 72 67 73 ch { |path| args
1440: 2e 63 6f 6e 63 61 74 28 5b 22 2d 2d 72 6f 2d 62 .concat(["--ro-b
1450: 69 6e 64 2d 74 72 79 22 2c 20 70 61 74 68 2c 20 ind-try", path,
1460: 70 61 74 68 5d 29 20 7d 0a 20 20 20 20 61 72 67 path]) }. arg
1470: 73 2e 63 6f 6e 63 61 74 28 5b 22 2d 2d 70 72 6f s.concat(["--pro
1480: 63 22 2c 20 22 2f 70 72 6f 63 22 2c 20 22 2d 2d c", "/proc", "--
1490: 64 65 76 22 2c 20 22 2f 64 65 76 22 2c 20 22 2d dev", "/dev", "-
14a0: 2d 74 6d 70 66 73 22 2c 20 22 2f 74 6d 70 22 2c -tmpfs", "/tmp",
14b0: 20 22 2d 2d 74 6d 70 66 73 22 2c 20 70 61 74 68 "--tmpfs", path
14c0: 73 2e 68 6f 6d 65 5d 29 0a 20 20 20 20 68 6f 6d s.home]). hom
14d0: 65 73 2e 65 61 63 68 20 7b 20 7c 73 6f 75 72 63 es.each { |sourc
14e0: 65 2c 20 64 65 73 74 2c 20 77 72 69 74 61 62 6c e, dest, writabl
14f0: 65 7c 20 61 72 67 73 2e 63 6f 6e 63 61 74 28 5b e| args.concat([
1500: 77 72 69 74 61 62 6c 65 20 3f 20 22 2d 2d 62 69 writable ? "--bi
1510: 6e 64 2d 74 72 79 22 20 3a 20 22 2d 2d 72 6f 2d nd-try" : "--ro-
1520: 62 69 6e 64 2d 74 72 79 22 2c 20 73 6f 75 72 63 bind-try", sourc
1530: 65 2c 20 64 65 73 74 5d 29 20 7d 0a 20 20 20 20 e, dest]) }.
1540: 61 72 67 73 2e 63 6f 6e 63 61 74 28 5b 22 2d 2d args.concat(["--
1550: 62 69 6e 64 22 2c 20 70 61 74 68 73 2e 64 61 65 bind", paths.dae
1560: 6d 6f 6e 2c 20 70 61 74 68 73 2e 64 61 65 6d 6f mon, paths.daemo
1570: 6e 2c 20 46 69 6c 65 2e 6a 6f 69 6e 28 70 61 74 n, File.join(pat
1580: 68 73 2e 64 61 65 6d 6f 6e 2c 20 22 63 75 72 72 hs.daemon, "curr
1590: 65 6e 74 22 2c 20 22 6f 63 78 32 22 29 2c 20 22 ent", "ocx2"), "
15a0: 64 61 65 6d 6f 6e 22 2c 20 46 69 6c 65 2e 6a 6f daemon", File.jo
15b0: 69 6e 28 70 61 74 68 73 2e 64 61 65 6d 6f 6e 2c in(paths.daemon,
15c0: 20 22 6e 6f 64 65 22 2c 20 22 6e 6f 64 65 73 2e "node", "nodes.
15d0: 6a 73 6f 6e 22 29 5d 29 0a 20 20 65 6e 64 0a 0a json")]). end..
15e0: 20 20 23 20 52 65 73 6f 6c 76 65 20 65 78 69 73 # Resolve exis
15f0: 74 69 6e 67 20 61 6e 63 65 73 74 6f 72 73 20 74 ting ancestors t
1600: 6f 6f 2c 20 73 6f 20 61 6e 20 61 62 73 65 6e 74 oo, so an absent
1610: 20 6c 65 61 66 20 63 61 6e 6e 6f 74 20 68 69 64 leaf cannot hid
1620: 65 20 61 20 73 79 6d 6c 69 6e 6b 2e 0a 20 20 64 e a symlink.. d
1630: 65 66 20 73 65 6c 66 2e 72 65 61 6c 5f 62 6f 75 ef self.real_bou
1640: 6e 64 28 70 61 74 68 20 3a 20 53 74 72 69 6e 67 nd(path : String
1650: 29 20 3a 20 53 74 72 69 6e 67 0a 20 20 20 20 72 ) : String. r
1660: 65 74 75 72 6e 20 46 69 6c 65 2e 72 65 61 6c 70 eturn File.realp
1670: 61 74 68 28 70 61 74 68 29 20 69 66 20 46 69 6c ath(path) if Fil
1680: 65 2e 65 78 69 73 74 73 3f 28 70 61 74 68 29 20 e.exists?(path)
1690: 7c 7c 20 46 69 6c 65 2e 73 79 6d 6c 69 6e 6b 3f || File.symlink?
16a0: 28 70 61 74 68 29 0a 20 20 20 20 65 78 70 61 6e (path). expan
16b0: 64 65 64 20 3d 20 46 69 6c 65 2e 65 78 70 61 6e ded = File.expan
16c0: 64 5f 70 61 74 68 28 70 61 74 68 29 0a 20 20 20 d_path(path).
16d0: 20 72 65 74 75 72 6e 20 65 78 70 61 6e 64 65 64 return expanded
16e0: 20 69 66 20 65 78 70 61 6e 64 65 64 20 3d 3d 20 if expanded ==
16f0: 22 2f 22 0a 20 20 20 20 46 69 6c 65 2e 6a 6f 69 "/". File.joi
1700: 6e 28 72 65 61 6c 5f 62 6f 75 6e 64 28 46 69 6c n(real_bound(Fil
1710: 65 2e 64 69 72 6e 61 6d 65 28 65 78 70 61 6e 64 e.dirname(expand
1720: 65 64 29 29 2c 20 46 69 6c 65 2e 62 61 73 65 6e ed)), File.basen
1730: 61 6d 65 28 65 78 70 61 6e 64 65 64 29 29 0a 20 ame(expanded)).
1740: 20 65 6e 64 0a 0a 20 20 64 65 66 20 73 65 6c 66 end.. def self
1750: 2e 67 75 61 72 64 5f 64 61 65 6d 6f 6e 28 61 72 .guard_daemon(ar
1760: 67 73 20 3a 20 41 72 72 61 79 28 53 74 72 69 6e gs : Array(Strin
1770: 67 29 2c 20 72 65 70 6f 73 69 74 6f 72 69 65 73 g), repositories
1780: 20 3a 20 53 74 72 69 6e 67 29 20 3a 20 4e 69 6c : String) : Nil
1790: 0a 20 20 20 20 72 65 70 6f 73 69 74 6f 72 79 20 . repository
17a0: 3d 20 72 65 61 6c 5f 62 6f 75 6e 64 28 72 65 70 = real_bound(rep
17b0: 6f 73 69 74 6f 72 69 65 73 29 2e 72 63 68 6f 70 ositories).rchop
17c0: 28 27 2f 27 29 0a 20 20 20 20 61 72 67 73 2e 65 ('/'). args.e
17d0: 61 63 68 5f 77 69 74 68 5f 69 6e 64 65 78 20 64 ach_with_index d
17e0: 6f 20 7c 61 72 67 2c 20 69 6e 64 65 78 7c 0a 20 o |arg, index|.
17f0: 20 20 20 20 20 23 20 6f 6e 6c 79 20 61 20 62 69 # only a bi
1800: 6e 64 20 62 72 69 6e 67 73 20 61 20 68 6f 73 74 nd brings a host
1810: 20 70 61 74 68 20 69 6e 3b 20 2d 2d 74 6d 70 66 path in; --tmpf
1820: 73 2c 20 2d 2d 70 72 6f 63 20 61 6e 64 20 2d 2d s, --proc and --
1830: 64 65 76 20 6d 61 6b 65 20 66 72 65 73 68 2c 20 dev make fresh,
1840: 65 6d 70 74 79 20 6f 6e 65 73 0a 20 20 20 20 20 empty ones.
1850: 20 6e 65 78 74 20 75 6e 6c 65 73 73 20 7b 22 2d next unless {"-
1860: 2d 62 69 6e 64 22 2c 20 22 2d 2d 62 69 6e 64 2d -bind", "--bind-
1870: 74 72 79 22 2c 20 22 2d 2d 72 6f 2d 62 69 6e 64 try", "--ro-bind
1880: 22 2c 20 22 2d 2d 72 6f 2d 62 69 6e 64 2d 74 72 ", "--ro-bind-tr
1890: 79 22 7d 2e 69 6e 63 6c 75 64 65 73 3f 28 61 72 y"}.includes?(ar
18a0: 67 29 0a 20 20 20 20 20 20 70 61 74 68 20 3d 20 g). path =
18b0: 72 65 61 6c 5f 62 6f 75 6e 64 28 61 72 67 73 5b real_bound(args[
18c0: 69 6e 64 65 78 20 2b 20 31 5d 29 2e 72 63 68 6f index + 1]).rcho
18d0: 70 28 27 2f 27 29 0a 20 20 20 20 20 20 69 66 20 p('/'). if
18e0: 70 61 74 68 20 3d 3d 20 72 65 70 6f 73 69 74 6f path == reposito
18f0: 72 79 20 7c 7c 20 70 61 74 68 2e 73 74 61 72 74 ry || path.start
1900: 73 5f 77 69 74 68 3f 28 72 65 70 6f 73 69 74 6f s_with?(reposito
1910: 72 79 20 2b 20 22 2f 22 29 20 7c 7c 20 72 65 70 ry + "/") || rep
1920: 6f 73 69 74 6f 72 79 2e 73 74 61 72 74 73 5f 77 ository.starts_w
1930: 69 74 68 3f 28 70 61 74 68 20 2b 20 22 2f 22 29 ith?(path + "/")
1940: 0a 20 20 20 20 20 20 20 20 72 61 69 73 65 20 41 . raise A
1950: 72 67 75 6d 65 6e 74 45 72 72 6f 72 2e 6e 65 77 rgumentError.new
1960: 28 22 64 61 65 6d 6f 6e 20 62 6f 75 6e 64 20 70 ("daemon bound p
1970: 61 74 68 20 6f 76 65 72 6c 61 70 73 20 73 65 72 ath overlaps ser
1980: 76 65 64 20 72 65 70 6f 73 69 74 6f 72 69 65 73 ved repositories
1990: 22 29 0a 20 20 20 20 20 20 65 6e 64 0a 20 20 20 "). end.
19a0: 20 65 6e 64 0a 20 20 65 6e 64 0a 0a 20 20 64 65 end. end.. de
19b0: 66 20 73 65 6c 66 2e 63 6c 6f 73 65 5f 69 6e 68 f self.close_inh
19c0: 65 72 69 74 65 64 20 3a 20 4e 69 6c 0a 20 20 20 erited : Nil.
19d0: 20 64 69 72 20 3d 20 4c 69 62 43 2e 6f 70 65 6e dir = LibC.open
19e0: 64 69 72 28 22 2f 70 72 6f 63 2f 73 65 6c 66 2f dir("/proc/self/
19f0: 66 64 22 29 0a 20 20 20 20 72 61 69 73 65 20 49 fd"). raise I
1a00: 4f 3a 3a 45 72 72 6f 72 2e 66 72 6f 6d 5f 65 72 O::Error.from_er
1a10: 72 6e 6f 28 22 6f 70 65 6e 64 69 72 22 29 20 69 rno("opendir") i
1a20: 66 20 64 69 72 2e 6e 75 6c 6c 3f 0a 20 20 20 20 f dir.null?.
1a30: 62 65 67 69 6e 0a 20 20 20 20 20 20 6f 77 6e 20 begin. own
1a40: 3d 20 4c 69 62 43 2e 64 69 72 66 64 28 64 69 72 = LibC.dirfd(dir
1a50: 29 0a 20 20 20 20 20 20 77 68 69 6c 65 20 65 6e ). while en
1a60: 74 72 79 20 3d 20 4c 69 62 43 2e 72 65 61 64 64 try = LibC.readd
1a70: 69 72 28 64 69 72 29 0a 20 20 20 20 20 20 20 20 ir(dir).
1a80: 66 64 20 3d 20 53 74 72 69 6e 67 2e 6e 65 77 28 fd = String.new(
1a90: 65 6e 74 72 79 2e 76 61 6c 75 65 2e 64 5f 6e 61 entry.value.d_na
1aa0: 6d 65 2e 74 6f 5f 75 6e 73 61 66 65 29 2e 74 6f me.to_unsafe).to
1ab0: 5f 69 3f 0a 20 20 20 20 20 20 20 20 4c 69 62 43 _i?. LibC
1ac0: 2e 63 6c 6f 73 65 28 66 64 29 20 69 66 20 66 64 .close(fd) if fd
1ad0: 20 26 26 20 66 64 20 3e 20 32 20 26 26 20 66 64 && fd > 2 && fd
1ae0: 20 21 3d 20 6f 77 6e 0a 20 20 20 20 20 20 65 6e != own. en
1af0: 64 0a 20 20 20 20 65 6e 73 75 72 65 0a 20 20 20 d. ensure.
1b00: 20 20 20 4c 69 62 43 2e 63 6c 6f 73 65 64 69 72 LibC.closedir
1b10: 28 64 69 72 29 0a 20 20 20 20 65 6e 64 0a 20 20 (dir). end.
1b20: 65 6e 64 0a 0a 20 20 64 65 66 20 73 65 6c 66 2e end.. def self.
1b30: 63 67 69 28 70 61 74 68 73 20 3a 20 50 61 74 68 cgi(paths : Path
1b40: 73 29 20 3a 20 4e 6f 52 65 74 75 72 6e 0a 20 20 s) : NoReturn.
1b50: 20 20 72 65 6c 65 61 73 65 20 3d 20 46 69 6c 65 release = File
1b60: 2e 72 65 61 6c 70 61 74 68 28 46 69 6c 65 2e 6a .realpath(File.j
1b70: 6f 69 6e 28 70 61 74 68 73 2e 72 65 6c 65 61 73 oin(paths.releas
1b80: 65 2c 20 22 63 75 72 72 65 6e 74 22 29 29 0a 20 e, "current")).
1b90: 20 20 20 63 6c 6f 73 65 5f 69 6e 68 65 72 69 74 close_inherit
1ba0: 65 64 0a 20 20 20 20 23 20 4d 69 72 72 6f 72 20 ed. # Mirror
1bb0: 73 72 63 2f 6f 63 78 2f 63 67 69 2e 63 72 27 73 src/ocx/cgi.cr's
1bc0: 20 72 75 6c 65 20 66 6f 72 20 74 68 65 20 74 6f rule for the to
1bd0: 6f 6c 20 61 66 74 65 72 20 74 68 65 20 70 72 6f ol after the pro
1be0: 67 72 61 6d 27 73 20 62 61 73 65 6e 61 6d 65 2e gram's basename.
1bf0: 0a 20 20 20 20 77 6f 72 64 73 20 3d 20 28 45 4e . words = (EN
1c00: 56 5b 22 50 41 54 48 5f 49 4e 46 4f 22 5d 3f 20 V["PATH_INFO"]?
1c10: 7c 7c 20 22 22 29 2e 73 70 6c 69 74 28 27 2f 27 || "").split('/'
1c20: 29 2e 72 65 6a 65 63 74 28 26 2e 65 6d 70 74 79 ).reject(&.empty
1c30: 3f 29 0a 20 20 20 20 6e 61 6d 65 20 3d 20 77 6f ?). name = wo
1c40: 72 64 73 5b 28 28 77 6f 72 64 73 2e 72 69 6e 64 rds[((words.rind
1c50: 65 78 28 46 69 6c 65 2e 62 61 73 65 6e 61 6d 65 ex(File.basename
1c60: 28 50 52 4f 47 52 41 4d 5f 4e 41 4d 45 29 29 20 (PROGRAM_NAME))
1c70: 7c 7c 20 2d 31 29 20 2b 20 31 29 2e 2e 5d 2e 66 || -1) + 1)..].f
1c80: 69 72 73 74 3f 0a 20 20 20 20 75 6e 6c 65 73 73 irst?. unless
1c90: 20 6e 61 6d 65 20 3d 3d 20 22 72 65 63 65 69 70 name == "receip
1ca0: 74 22 0a 20 20 20 20 20 20 23 20 45 36 3a 20 74 t". # E6: t
1cb0: 68 69 73 20 70 61 73 73 74 68 72 6f 75 67 68 20 his passthrough
1cc0: 67 6f 65 73 20 77 68 65 6e 20 74 68 65 20 43 47 goes when the CG
1cd0: 49 20 74 61 6c 6b 73 20 74 6f 20 74 68 65 20 64 I talks to the d
1ce0: 61 65 6d 6f 6e 2e 0a 20 20 20 20 20 20 65 78 65 aemon.. exe
1cf0: 63 75 74 61 62 6c 65 20 3d 20 46 69 6c 65 2e 6a cutable = File.j
1d00: 6f 69 6e 28 72 65 6c 65 61 73 65 2c 20 22 6f 63 oin(release, "oc
1d10: 78 32 22 29 0a 20 20 20 20 20 20 61 72 67 76 20 x2"). argv
1d20: 3d 20 5b 46 69 6c 65 2e 6a 6f 69 6e 28 72 65 6c = [File.join(rel
1d30: 65 61 73 65 2c 20 22 6f 63 78 32 2d 63 67 69 22 ease, "ocx2-cgi"
1d40: 29 2e 74 6f 5f 75 6e 73 61 66 65 2c 20 50 6f 69 ).to_unsafe, Poi
1d50: 6e 74 65 72 28 55 49 6e 74 38 29 2e 6e 75 6c 6c nter(UInt8).null
1d60: 5d 0a 20 20 20 20 20 20 4c 69 62 43 2e 65 78 65 ]. LibC.exe
1d70: 63 76 28 65 78 65 63 75 74 61 62 6c 65 2c 20 61 cv(executable, a
1d80: 72 67 76 2e 74 6f 5f 75 6e 73 61 66 65 29 0a 20 rgv.to_unsafe).
1d90: 20 20 20 20 20 72 61 69 73 65 20 49 4f 3a 3a 45 raise IO::E
1da0: 72 72 6f 72 2e 66 72 6f 6d 5f 65 72 72 6e 6f 28 rror.from_errno(
1db0: 22 65 78 65 63 76 22 29 0a 20 20 20 20 65 6e 64 "execv"). end
1dc0: 0a 20 20 20 20 72 65 70 6f 73 69 74 6f 72 79 20 . repository
1dd0: 3d 20 46 69 6c 65 2e 62 61 73 65 6e 61 6d 65 28 = File.basename(
1de0: 45 4e 56 5b 22 46 4f 53 53 49 4c 5f 52 45 50 4f ENV["FOSSIL_REPO
1df0: 53 49 54 4f 52 59 22 5d 3f 20 7c 7c 20 22 22 2c SITORY"]? || "",
1e00: 20 22 2e 66 6f 73 73 69 6c 22 29 0a 20 20 20 20 ".fossil").
1e10: 62 65 67 69 6e 0a 20 20 20 20 20 20 23 20 55 73 begin. # Us
1e20: 65 20 62 6c 6f 63 6b 69 6e 67 20 73 79 73 63 61 e blocking sysca
1e30: 6c 6c 73 20 61 66 74 65 72 20 63 6c 6f 73 69 6e lls after closin
1e40: 67 20 64 65 73 63 72 69 70 74 6f 72 73 2c 20 69 g descriptors, i
1e50: 6e 63 6c 75 64 69 6e 67 20 72 75 6e 74 69 6d 65 ncluding runtime
1e60: 20 65 76 65 6e 74 20 66 64 73 2e 0a 20 20 20 20 event fds..
1e70: 20 20 61 64 64 72 65 73 73 20 3d 20 53 6f 63 6b address = Sock
1e80: 65 74 3a 3a 55 4e 49 58 41 64 64 72 65 73 73 2e et::UNIXAddress.
1e90: 6e 65 77 28 70 61 74 68 73 2e 73 6f 63 6b 65 74 new(paths.socket
1ea0: 29 0a 20 20 20 20 20 20 66 64 20 3d 20 4c 69 62 ). fd = Lib
1eb0: 43 2e 73 6f 63 6b 65 74 28 4c 69 62 43 3a 3a 41 C.socket(LibC::A
1ec0: 46 5f 55 4e 49 58 2c 20 4c 69 62 43 3a 3a 53 4f F_UNIX, LibC::SO
1ed0: 43 4b 5f 53 54 52 45 41 4d 2c 20 30 29 0a 20 20 CK_STREAM, 0).
1ee0: 20 20 20 20 72 61 69 73 65 20 49 4f 3a 3a 45 72 raise IO::Er
1ef0: 72 6f 72 2e 66 72 6f 6d 5f 65 72 72 6e 6f 28 22 ror.from_errno("
1f00: 73 6f 63 6b 65 74 22 29 20 69 66 20 66 64 20 3d socket") if fd =
1f10: 3d 20 2d 31 0a 20 20 20 20 20 20 23 20 4c 69 6e = -1. # Lin
1f20: 75 78 20 62 6f 75 6e 64 73 20 62 6f 74 68 20 63 ux bounds both c
1f30: 6f 6e 6e 65 63 74 20 74 6f 20 61 20 66 75 6c 6c onnect to a full
1f40: 20 55 6e 69 78 20 6c 69 73 74 65 6e 65 72 20 71 Unix listener q
1f50: 75 65 75 65 20 61 6e 64 20 74 68 65 20 69 64 65 ueue and the ide
1f60: 6e 74 69 74 79 20 77 72 69 74 65 2e 0a 20 20 20 ntity write..
1f70: 20 20 20 74 69 6d 65 6f 75 74 20 3d 20 4c 69 62 timeout = Lib
1f80: 43 3a 3a 54 69 6d 65 76 61 6c 2e 6e 65 77 28 74 C::Timeval.new(t
1f90: 76 5f 73 65 63 3a 20 32 2c 20 74 76 5f 75 73 65 v_sec: 2, tv_use
1fa0: 63 3a 20 30 29 0a 20 20 20 20 20 20 72 61 69 73 c: 0). rais
1fb0: 65 20 49 4f 3a 3a 45 72 72 6f 72 2e 66 72 6f 6d e IO::Error.from
1fc0: 5f 65 72 72 6e 6f 28 22 73 65 74 73 6f 63 6b 6f _errno("setsocko
1fd0: 70 74 22 29 20 69 66 20 4c 69 62 43 2e 73 65 74 pt") if LibC.set
1fe0: 73 6f 63 6b 6f 70 74 28 66 64 2c 20 4c 69 62 43 sockopt(fd, LibC
1ff0: 3a 3a 53 4f 4c 5f 53 4f 43 4b 45 54 2c 20 4c 69 ::SOL_SOCKET, Li
2000: 62 43 3a 3a 53 4f 5f 53 4e 44 54 49 4d 45 4f 2c bC::SO_SNDTIMEO,
2010: 20 70 6f 69 6e 74 65 72 6f 66 28 74 69 6d 65 6f pointerof(timeo
2020: 75 74 29 2c 20 73 69 7a 65 6f 66 28 4c 69 62 43 ut), sizeof(LibC
2030: 3a 3a 54 69 6d 65 76 61 6c 29 29 20 3d 3d 20 2d ::Timeval)) == -
2040: 31 0a 20 20 20 20 20 20 72 61 69 73 65 20 49 4f 1. raise IO
2050: 3a 3a 45 72 72 6f 72 2e 66 72 6f 6d 5f 65 72 72 ::Error.from_err
2060: 6e 6f 28 22 63 6f 6e 6e 65 63 74 22 29 20 69 66 no("connect") if
2070: 20 4c 69 62 43 2e 63 6f 6e 6e 65 63 74 28 66 64 LibC.connect(fd
2080: 2c 20 61 64 64 72 65 73 73 2c 20 61 64 64 72 65 , address, addre
2090: 73 73 2e 73 69 7a 65 29 20 3d 3d 20 2d 31 0a 20 ss.size) == -1.
20a0: 20 20 20 20 20 69 64 65 6e 74 69 74 79 20 3d 20 identity =
20b0: 4a 53 4f 4e 2e 62 75 69 6c 64 20 64 6f 20 7c 6a JSON.build do |j
20c0: 73 6f 6e 7c 0a 20 20 20 20 20 20 20 20 6a 73 6f son|. jso
20d0: 6e 2e 6f 62 6a 65 63 74 20 64 6f 0a 20 20 20 20 n.object do.
20e0: 20 20 20 20 20 20 69 66 20 75 73 65 72 20 3d 20 if user =
20f0: 45 4e 56 5b 22 46 4f 53 53 49 4c 5f 55 53 45 52 ENV["FOSSIL_USER
2100: 22 5d 3f 0a 20 20 20 20 20 20 20 20 20 20 20 20 "]?.
2110: 6a 73 6f 6e 2e 66 69 65 6c 64 20 22 75 73 65 72 json.field "user
2120: 22 2c 20 75 73 65 72 0a 20 20 20 20 20 20 20 20 ", user.
2130: 20 20 65 6e 64 0a 20 20 20 20 20 20 20 20 20 20 end.
2140: 6a 73 6f 6e 2e 66 69 65 6c 64 20 22 63 61 70 61 json.field "capa
2150: 62 69 6c 69 74 69 65 73 22 2c 20 45 4e 56 5b 22 bilities", ENV["
2160: 46 4f 53 53 49 4c 5f 43 41 50 41 42 49 4c 49 54 FOSSIL_CAPABILIT
2170: 49 45 53 22 5d 3f 20 7c 7c 20 22 22 0a 20 20 20 IES"]? || "".
2180: 20 20 20 20 20 20 20 6a 73 6f 6e 2e 66 69 65 6c json.fiel
2190: 64 20 22 72 65 70 6f 73 69 74 6f 72 79 22 2c 20 d "repository",
21a0: 72 65 70 6f 73 69 74 6f 72 79 0a 20 20 20 20 20 repository.
21b0: 20 20 20 65 6e 64 0a 20 20 20 20 20 20 65 6e 64 end. end
21c0: 0a 20 20 20 20 20 20 62 79 74 65 73 20 3d 20 28 . bytes = (
21d0: 69 64 65 6e 74 69 74 79 20 2b 20 22 5c 6e 22 29 identity + "\n")
21e0: 2e 74 6f 5f 73 6c 69 63 65 0a 20 20 20 20 20 20 .to_slice.
21f0: 75 6e 74 69 6c 20 62 79 74 65 73 2e 65 6d 70 74 until bytes.empt
2200: 79 3f 0a 20 20 20 20 20 20 20 20 77 72 69 74 74 y?. writt
2210: 65 6e 20 3d 20 4c 69 62 43 2e 77 72 69 74 65 28 en = LibC.write(
2220: 66 64 2c 20 62 79 74 65 73 2c 20 62 79 74 65 73 fd, bytes, bytes
2230: 2e 73 69 7a 65 29 0a 20 20 20 20 20 20 20 20 6e .size). n
2240: 65 78 74 20 69 66 20 77 72 69 74 74 65 6e 20 3d ext if written =
2250: 3d 20 2d 31 20 26 26 20 45 72 72 6e 6f 2e 76 61 = -1 && Errno.va
2260: 6c 75 65 20 3d 3d 20 45 72 72 6e 6f 3a 3a 45 49 lue == Errno::EI
2270: 4e 54 52 0a 20 20 20 20 20 20 20 20 72 61 69 73 NTR. rais
2280: 65 20 49 4f 3a 3a 45 72 72 6f 72 2e 66 72 6f 6d e IO::Error.from
2290: 5f 65 72 72 6e 6f 28 22 77 72 69 74 65 22 29 20 _errno("write")
22a0: 69 66 20 77 72 69 74 74 65 6e 20 3c 3d 20 30 0a if written <= 0.
22b0: 20 20 20 20 20 20 20 20 62 79 74 65 73 20 3d 20 bytes =
22c0: 62 79 74 65 73 5b 77 72 69 74 74 65 6e 2e 74 6f bytes[written.to
22d0: 5f 69 2e 2e 5d 0a 20 20 20 20 20 20 65 6e 64 0a _i..]. end.
22e0: 20 20 20 20 72 65 73 63 75 65 20 53 6f 63 6b 65 rescue Socke
22f0: 74 3a 3a 45 72 72 6f 72 20 7c 20 49 4f 3a 3a 45 t::Error | IO::E
2300: 72 72 6f 72 0a 20 20 20 20 20 20 23 20 54 68 65 rror. # The
2310: 20 72 75 6e 74 69 6d 65 27 73 20 6f 77 6e 20 64 runtime's own d
2320: 65 73 63 72 69 70 74 6f 72 73 20 61 72 65 20 63 escriptors are c
2330: 6c 6f 73 65 64 2c 20 73 6f 20 77 72 69 74 65 20 losed, so write
2340: 77 69 74 68 20 74 68 65 20 73 79 73 74 65 6d 20 with the system
2350: 63 61 6c 6c 20 69 74 73 65 6c 66 2e 0a 20 20 20 call itself..
2360: 20 20 20 61 6e 73 77 65 72 20 3d 20 22 53 74 61 answer = "Sta
2370: 74 75 73 3a 20 35 30 33 5c 72 5c 6e 43 6f 6e 74 tus: 503\r\nCont
2380: 65 6e 74 2d 54 79 70 65 3a 20 61 70 70 6c 69 63 ent-Type: applic
2390: 61 74 69 6f 6e 2f 6a 73 6f 6e 5c 72 5c 6e 5c 72 ation/json\r\n\r
23a0: 5c 6e 7b 5c 22 75 6e 61 76 61 69 6c 61 62 6c 65 \n{\"unavailable
23b0: 5c 22 3a 5c 22 72 65 63 65 69 70 74 20 73 65 72 \":\"receipt ser
23c0: 76 69 63 65 20 6e 6f 74 20 61 6e 73 77 65 72 69 vice not answeri
23d0: 6e 67 5c 22 7d 5c 6e 22 2e 74 6f 5f 73 6c 69 63 ng\"}\n".to_slic
23e0: 65 0a 20 20 20 20 20 20 4c 69 62 43 2e 77 72 69 e. LibC.wri
23f0: 74 65 28 31 2c 20 61 6e 73 77 65 72 2c 20 61 6e te(1, answer, an
2400: 73 77 65 72 2e 73 69 7a 65 29 0a 20 20 20 20 20 swer.size).
2410: 20 65 78 69 74 20 30 0a 20 20 20 20 65 6e 64 0a exit 0. end.
2420: 20 20 20 20 72 61 69 73 65 20 49 4f 3a 3a 45 72 raise IO::Er
2430: 72 6f 72 2e 66 72 6f 6d 5f 65 72 72 6e 6f 28 22 ror.from_errno("
2440: 64 75 70 32 22 29 20 69 66 20 4c 69 62 43 2e 64 dup2") if LibC.d
2450: 75 70 32 28 66 64 2c 20 33 29 20 3d 3d 20 2d 31 up2(fd, 3) == -1
2460: 0a 20 20 20 20 4c 69 62 43 2e 63 6c 6f 73 65 28 . LibC.close(
2470: 66 64 29 20 75 6e 6c 65 73 73 20 66 64 20 3d 3d fd) unless fd ==
2480: 20 33 0a 20 20 20 20 72 61 69 73 65 20 49 4f 3a 3. raise IO:
2490: 3a 45 72 72 6f 72 2e 66 72 6f 6d 5f 65 72 72 6e :Error.from_errn
24a0: 6f 28 22 66 63 6e 74 6c 22 29 20 69 66 20 4c 69 o("fcntl") if Li
24b0: 62 43 2e 66 63 6e 74 6c 28 33 2c 20 4c 69 62 43 bC.fcntl(3, LibC
24c0: 3a 3a 46 5f 53 45 54 46 44 2c 20 30 29 20 3d 3d ::F_SETFD, 0) ==
24d0: 20 2d 31 0a 20 20 20 20 65 6e 76 69 72 6f 6e 6d -1. environm
24e0: 65 6e 74 20 3d 20 7b 7d 20 6f 66 20 53 74 72 69 ent = {} of Stri
24f0: 6e 67 20 3d 3e 20 53 74 72 69 6e 67 0a 20 20 20 ng => String.
2500: 20 43 47 49 5f 46 49 45 4c 44 53 2e 65 61 63 68 CGI_FIELDS.each
2510: 20 64 6f 20 7c 6b 65 79 7c 0a 20 20 20 20 20 20 do |key|.
2520: 69 66 20 76 61 6c 75 65 20 3d 20 45 4e 56 5b 6b if value = ENV[k
2530: 65 79 5d 3f 0a 20 20 20 20 20 20 20 20 65 6e 76 ey]?. env
2540: 69 72 6f 6e 6d 65 6e 74 5b 6b 65 79 5d 20 3d 20 ironment[key] =
2550: 76 61 6c 75 65 0a 20 20 20 20 20 20 65 6e 64 0a value. end.
2560: 20 20 20 20 65 6e 64 0a 20 20 20 20 65 78 65 63 end. exec
2570: 5f 72 61 77 28 22 2f 75 73 72 2f 62 69 6e 2f 62 _raw("/usr/bin/b
2580: 77 72 61 70 22 2c 20 63 67 69 5f 61 72 67 73 28 wrap", cgi_args(
2590: 72 65 6c 65 61 73 65 2c 20 72 65 70 6f 73 69 74 release, reposit
25a0: 6f 72 79 2c 20 65 6e 76 69 72 6f 6e 6d 65 6e 74 ory, environment
25b0: 29 29 0a 20 20 65 6e 64 0a 0a 20 20 23 20 65 78 )). end.. # ex
25c0: 65 63 76 20 77 69 74 68 6f 75 74 20 74 68 65 20 ecv without the
25d0: 72 75 6e 74 69 6d 65 2c 20 77 68 6f 73 65 20 64 runtime, whose d
25e0: 65 73 63 72 69 70 74 6f 72 73 20 63 6c 6f 73 65 escriptors close
25f0: 5f 69 6e 68 65 72 69 74 65 64 20 68 61 73 20 63 _inherited has c
2600: 6c 6f 73 65 64 2e 0a 20 20 64 65 66 20 73 65 6c losed.. def sel
2610: 66 2e 65 78 65 63 5f 72 61 77 28 70 72 6f 67 72 f.exec_raw(progr
2620: 61 6d 20 3a 20 53 74 72 69 6e 67 2c 20 61 72 67 am : String, arg
2630: 73 20 3a 20 41 72 72 61 79 28 53 74 72 69 6e 67 s : Array(String
2640: 29 29 20 3a 20 4e 6f 52 65 74 75 72 6e 0a 20 20 )) : NoReturn.
2650: 20 20 61 72 67 76 20 3d 20 28 5b 70 72 6f 67 72 argv = ([progr
2660: 61 6d 5d 20 2b 20 61 72 67 73 29 2e 6d 61 70 28 am] + args).map(
2670: 26 2e 74 6f 5f 75 6e 73 61 66 65 29 20 2b 20 5b &.to_unsafe) + [
2680: 50 6f 69 6e 74 65 72 28 55 49 6e 74 38 29 2e 6e Pointer(UInt8).n
2690: 75 6c 6c 5d 0a 20 20 20 20 4c 69 62 43 2e 65 78 ull]. LibC.ex
26a0: 65 63 76 28 70 72 6f 67 72 61 6d 2c 20 61 72 67 ecv(program, arg
26b0: 76 2e 74 6f 5f 75 6e 73 61 66 65 29 0a 20 20 20 v.to_unsafe).
26c0: 20 72 61 69 73 65 20 49 4f 3a 3a 45 72 72 6f 72 raise IO::Error
26d0: 2e 66 72 6f 6d 5f 65 72 72 6e 6f 28 22 65 78 65 .from_errno("exe
26e0: 63 76 22 29 0a 20 20 65 6e 64 0a 0a 20 20 64 65 cv"). end.. de
26f0: 66 20 73 65 6c 66 2e 72 75 6e 20 3a 20 4e 6f 52 f self.run : NoR
2700: 65 74 75 72 6e 0a 20 20 20 20 61 72 67 73 20 3d eturn. args =
2710: 20 41 52 47 56 2e 64 75 70 0a 20 20 20 20 73 63 ARGV.dup. sc
2720: 72 61 74 63 68 20 3d 20 6e 69 6c 0a 20 20 20 20 ratch = nil.
2730: 69 66 20 61 72 67 73 2e 66 69 72 73 74 3f 20 3d if args.first? =
2740: 3d 20 22 2d 2d 73 63 72 61 74 63 68 22 0a 20 20 = "--scratch".
2750: 20 20 20 20 61 72 67 73 2e 73 68 69 66 74 0a 20 args.shift.
2760: 20 20 20 20 20 73 63 72 61 74 63 68 20 3d 20 61 scratch = a
2770: 72 67 73 2e 73 68 69 66 74 3f 20 7c 7c 20 72 61 rgs.shift? || ra
2780: 69 73 65 20 41 72 67 75 6d 65 6e 74 45 72 72 6f ise ArgumentErro
2790: 72 2e 6e 65 77 28 22 6d 69 73 73 69 6e 67 20 73 r.new("missing s
27a0: 63 72 61 74 63 68 20 64 69 72 65 63 74 6f 72 79 cratch directory
27b0: 22 29 0a 20 20 20 20 65 6e 64 0a 20 20 20 20 63 "). end. c
27c0: 67 69 5f 6d 6f 64 65 20 3d 20 46 69 6c 65 2e 62 gi_mode = File.b
27d0: 61 73 65 6e 61 6d 65 28 50 52 4f 47 52 41 4d 5f asename(PROGRAM_
27e0: 4e 41 4d 45 29 20 3d 3d 20 22 6f 63 78 32 2d 63 NAME) == "ocx2-c
27f0: 67 69 22 0a 20 20 20 20 75 6e 6c 65 73 73 20 28 gi". unless (
2800: 63 67 69 5f 6d 6f 64 65 20 26 26 20 61 72 67 73 cgi_mode && args
2810: 2e 65 6d 70 74 79 3f 29 20 7c 7c 20 28 21 63 67 .empty?) || (!cg
2820: 69 5f 6d 6f 64 65 20 26 26 20 61 72 67 73 2e 73 i_mode && args.s
2830: 69 7a 65 20 3d 3d 20 31 20 26 26 20 7b 22 72 65 ize == 1 && {"re
2840: 63 65 69 70 74 73 22 2c 20 22 64 61 65 6d 6f 6e ceipts", "daemon
2850: 22 7d 2e 69 6e 63 6c 75 64 65 73 3f 28 61 72 67 "}.includes?(arg
2860: 73 5b 30 5d 29 29 0a 20 20 20 20 20 20 72 61 69 s[0])). rai
2870: 73 65 20 41 72 67 75 6d 65 6e 74 45 72 72 6f 72 se ArgumentError
2880: 2e 6e 65 77 28 22 75 6e 72 65 63 6f 67 6e 69 73 .new("unrecognis
2890: 65 64 20 6d 6f 64 65 22 29 0a 20 20 20 20 65 6e ed mode"). en
28a0: 64 0a 20 20 20 20 70 61 74 68 73 20 3d 20 70 61 d. paths = pa
28b0: 74 68 73 28 73 63 72 61 74 63 68 2c 20 53 79 73 ths(scratch, Sys
28c0: 74 65 6d 2e 68 6f 73 74 6e 61 6d 65 29 0a 20 20 tem.hostname).
28d0: 20 20 63 67 69 28 70 61 74 68 73 29 20 69 66 20 cgi(paths) if
28e0: 63 67 69 5f 6d 6f 64 65 0a 20 20 20 20 65 78 65 cgi_mode. exe
28f0: 63 75 74 61 62 6c 65 20 3d 20 50 72 6f 63 65 73 cutable = Proces
2900: 73 2e 65 78 65 63 75 74 61 62 6c 65 5f 70 61 74 s.executable_pat
2910: 68 20 7c 7c 20 72 61 69 73 65 20 41 72 67 75 6d h || raise Argum
2920: 65 6e 74 45 72 72 6f 72 2e 6e 65 77 28 22 63 61 entError.new("ca
2930: 6e 6e 6f 74 20 6c 6f 63 61 74 65 20 65 78 65 63 nnot locate exec
2940: 75 74 61 62 6c 65 22 29 0a 20 20 20 20 69 66 20 utable"). if
2950: 61 72 67 73 5b 30 5d 20 3d 3d 20 22 72 65 63 65 args[0] == "rece
2960: 69 70 74 73 22 0a 20 20 20 20 20 20 63 6f 6d 6d ipts". comm
2970: 61 6e 64 20 3d 20 72 65 63 65 69 70 74 73 5f 61 and = receipts_a
2980: 72 67 73 28 70 61 74 68 73 2c 20 46 69 6c 65 2e rgs(paths, File.
2990: 64 69 72 6e 61 6d 65 28 65 78 65 63 75 74 61 62 dirname(executab
29a0: 6c 65 29 29 0a 20 20 20 20 65 6c 73 65 0a 20 20 le)). else.
29b0: 20 20 20 20 63 6f 6d 6d 61 6e 64 20 3d 20 64 61 command = da
29c0: 65 6d 6f 6e 5f 61 72 67 73 28 70 61 74 68 73 2c emon_args(paths,
29d0: 20 70 61 74 68 73 2e 68 6f 6d 65 73 2e 73 65 6c paths.homes.sel
29e0: 65 63 74 20 7b 20 7c 73 6f 75 72 63 65 2c 20 5f ect { |source, _
29f0: 2c 20 5f 7c 20 46 69 6c 65 2e 65 78 69 73 74 73 , _| File.exists
2a00: 3f 28 73 6f 75 72 63 65 29 20 7d 29 0a 20 20 20 ?(source) }).
2a10: 20 20 20 67 75 61 72 64 5f 64 61 65 6d 6f 6e 28 guard_daemon(
2a20: 63 6f 6d 6d 61 6e 64 2c 20 70 61 74 68 73 2e 72 command, paths.r
2a30: 65 70 6f 73 69 74 6f 72 69 65 73 29 0a 20 20 20 epositories).
2a40: 20 65 6e 64 0a 20 20 20 20 50 72 6f 63 65 73 73 end. Process
2a50: 2e 65 78 65 63 28 22 2f 75 73 72 2f 62 69 6e 2f .exec("/usr/bin/
2a60: 62 77 72 61 70 22 2c 20 63 6f 6d 6d 61 6e 64 29 bwrap", command)
2a70: 0a 20 20 72 65 73 63 75 65 20 65 78 20 3a 20 41 . rescue ex : A
2a80: 72 67 75 6d 65 6e 74 45 72 72 6f 72 20 7c 20 49 rgumentError | I
2a90: 4f 3a 3a 45 72 72 6f 72 0a 20 20 20 20 53 54 44 O::Error. STD
2aa0: 45 52 52 2e 70 75 74 73 20 22 23 7b 65 78 2e 6d ERR.puts "#{ex.m
2ab0: 65 73 73 61 67 65 7d 5c 6e 55 73 61 67 65 3a 20 essage}\nUsage:
2ac0: 73 74 61 72 74 2d 6f 63 78 20 5b 2d 2d 73 63 72 start-ocx [--scr
2ad0: 61 74 63 68 20 44 49 52 5d 20 72 65 63 65 69 70 atch DIR] receip
2ae0: 74 73 7c 64 61 65 6d 6f 6e 3b 20 6f 63 78 32 2d ts|daemon; ocx2-
2af0: 63 67 69 20 5b 2d 2d 73 63 72 61 74 63 68 20 44 cgi [--scratch D
2b00: 49 52 5d 22 0a 20 20 20 20 65 78 69 74 20 32 0a IR]". exit 2.
2b10: 20 20 65 6e 64 0a 65 6e 64 0a 0a 53 74 61 72 74 end.end..Start
2b20: 4f 43 58 2e 72 75 6e 0a 00 00 00 00 00 00 00 00 OCX.run.........
2b30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2b40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2b50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2b60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2b70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2b90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2ba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2bb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2bd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2be0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2c00: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69 scripts-for-revi
2c10: 65 77 2d 32 2f 72 65 63 65 69 70 74 73 2e 63 72 ew-2/receipts.cr
2c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2c40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2c50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2c60: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30 ....0000644.0000
2c70: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 000.0000000.0000
2c80: 30 30 33 32 30 35 35 00 31 35 32 37 30 35 32 33 0032055.15270523
2c90: 31 32 32 00 30 31 35 36 33 35 00 20 30 00 00 00 122.015635. 0...
2ca0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2cb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2cc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2cd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2ce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2d00: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00 .ustar .root...
2d10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2d20: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00 .........root...
2d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2d70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2dd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2de0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2e00: 23 20 41 67 65 6e 74 73 3a 20 64 6f 20 6e 6f 74 # Agents: do not
2e10: 20 63 68 61 6e 67 65 20 74 68 69 73 20 66 69 6c change this fil
2e20: 65 20 6f 6e 20 61 6e 79 20 62 72 61 6e 63 68 20 e on any branch
2e30: 77 69 74 68 6f 75 74 20 74 68 65 20 6d 61 69 6e without the main
2e40: 74 61 69 6e 65 72 27 73 20 65 78 70 6c 69 63 69 tainer's explici
2e50: 74 20 61 70 70 72 6f 76 61 6c 20 6f 6e 20 74 68 t approval on th
2e60: 65 20 72 65 63 6f 72 64 2e 0a 23 20 53 65 6c 66 e record..# Self
2e70: 2d 63 6f 6e 74 61 69 6e 65 64 2c 20 73 74 61 6e -contained, stan
2e80: 64 61 72 64 2d 6c 69 62 72 61 72 79 2d 6f 6e 6c dard-library-onl
2e90: 79 20 72 65 63 65 69 70 74 20 73 65 72 76 69 63 y receipt servic
2ea0: 65 2e 20 55 73 61 67 65 3a 20 72 65 63 65 69 70 e. Usage: receip
2eb0: 74 73 20 53 4f 43 4b 45 54 20 52 45 50 4f 53 49 ts SOCKET REPOSI
2ec0: 54 4f 52 49 45 53 2e 0a 23 20 52 65 61 64 73 20 TORIES..# Reads
2ed0: 6f 6e 6c 79 20 69 74 73 20 74 77 6f 20 4a 53 4f only its two JSO
2ee0: 4e 20 6c 69 6e 65 73 20 61 6e 64 20 52 45 50 4f N lines and REPO
2ef0: 53 49 54 4f 52 49 45 53 2f 4e 41 4d 45 2e 66 6f SITORIES/NAME.fo
2f00: 73 73 69 6c 2c 20 72 65 61 64 2d 6f 6e 6c 79 20 ssil, read-only
2f10: 74 68 72 6f 75 67 68 20 53 51 4c 69 74 65 2e 0a through SQLite..
2f20: 72 65 71 75 69 72 65 20 22 6a 73 6f 6e 22 0a 72 require "json".r
2f30: 65 71 75 69 72 65 20 22 73 6f 63 6b 65 74 22 0a equire "socket".
2f40: 72 65 71 75 69 72 65 20 22 75 72 69 22 0a 72 65 require "uri".re
2f50: 71 75 69 72 65 20 22 73 65 74 22 0a 0a 6c 69 62 quire "set"..lib
2f60: 20 52 65 63 65 69 70 74 50 65 65 72 0a 20 20 73 ReceiptPeer. s
2f70: 74 72 75 63 74 20 43 72 65 64 65 6e 74 69 61 6c truct Credential
2f80: 73 0a 20 20 20 20 70 69 64 20 3a 20 49 6e 74 33 s. pid : Int3
2f90: 32 0a 20 20 20 20 75 69 64 20 3a 20 55 49 6e 74 2. uid : UInt
2fa0: 33 32 0a 20 20 20 20 67 69 64 20 3a 20 55 49 6e 32. gid : UIn
2fb0: 74 33 32 0a 20 20 65 6e 64 0a 0a 20 20 66 75 6e t32. end.. fun
2fc0: 20 67 65 74 73 6f 63 6b 6f 70 74 20 3d 20 67 65 getsockopt = ge
2fd0: 74 73 6f 63 6b 6f 70 74 28 66 64 20 3a 20 49 6e tsockopt(fd : In
2fe0: 74 33 32 2c 20 6c 65 76 65 6c 20 3a 20 49 6e 74 t32, level : Int
2ff0: 33 32 2c 20 6f 70 74 69 6f 6e 20 3a 20 49 6e 74 32, option : Int
3000: 33 32 2c 20 76 61 6c 75 65 20 3a 20 56 6f 69 64 32, value : Void
3010: 2a 2c 20 73 69 7a 65 20 3a 20 55 49 6e 74 33 32 *, size : UInt32
3020: 2a 29 20 3a 20 49 6e 74 33 32 0a 65 6e 64 0a 0a *) : Int32.end..
3030: 40 5b 4c 69 6e 6b 28 22 73 71 6c 69 74 65 33 22 @[Link("sqlite3"
3040: 29 5d 0a 6c 69 62 20 4c 69 62 53 51 4c 69 74 65 )].lib LibSQLite
3050: 33 0a 20 20 66 75 6e 20 6f 70 65 6e 5f 76 32 20 3. fun open_v2
3060: 3d 20 73 71 6c 69 74 65 33 5f 6f 70 65 6e 5f 76 = sqlite3_open_v
3070: 32 28 70 61 74 68 20 3a 20 55 49 6e 74 38 2a 2c 2(path : UInt8*,
3080: 20 64 62 20 3a 20 56 6f 69 64 2a 2a 2c 20 66 6c db : Void**, fl
3090: 61 67 73 20 3a 20 49 6e 74 33 32 2c 20 76 66 73 ags : Int32, vfs
30a0: 20 3a 20 55 49 6e 74 38 2a 29 20 3a 20 49 6e 74 : UInt8*) : Int
30b0: 33 32 0a 20 20 66 75 6e 20 63 6c 6f 73 65 5f 76 32. fun close_v
30c0: 32 20 3d 20 73 71 6c 69 74 65 33 5f 63 6c 6f 73 2 = sqlite3_clos
30d0: 65 5f 76 32 28 64 62 20 3a 20 56 6f 69 64 2a 29 e_v2(db : Void*)
30e0: 20 3a 20 49 6e 74 33 32 0a 20 20 66 75 6e 20 70 : Int32. fun p
30f0: 72 65 70 61 72 65 5f 76 32 20 3d 20 73 71 6c 69 repare_v2 = sqli
3100: 74 65 33 5f 70 72 65 70 61 72 65 5f 76 32 28 64 te3_prepare_v2(d
3110: 62 20 3a 20 56 6f 69 64 2a 2c 20 73 71 6c 20 3a b : Void*, sql :
3120: 20 55 49 6e 74 38 2a 2c 20 73 69 7a 65 20 3a 20 UInt8*, size :
3130: 49 6e 74 33 32 2c 20 73 74 61 74 65 6d 65 6e 74 Int32, statement
3140: 20 3a 20 56 6f 69 64 2a 2a 2c 20 74 61 69 6c 20 : Void**, tail
3150: 3a 20 55 49 6e 74 38 2a 2a 29 20 3a 20 49 6e 74 : UInt8**) : Int
3160: 33 32 0a 20 20 66 75 6e 20 62 69 6e 64 5f 74 65 32. fun bind_te
3170: 78 74 20 3d 20 73 71 6c 69 74 65 33 5f 62 69 6e xt = sqlite3_bin
3180: 64 5f 74 65 78 74 28 73 74 61 74 65 6d 65 6e 74 d_text(statement
3190: 20 3a 20 56 6f 69 64 2a 2c 20 69 6e 64 65 78 20 : Void*, index
31a0: 3a 20 49 6e 74 33 32 2c 20 74 65 78 74 20 3a 20 : Int32, text :
31b0: 55 49 6e 74 38 2a 2c 20 73 69 7a 65 20 3a 20 49 UInt8*, size : I
31c0: 6e 74 33 32 2c 20 64 65 73 74 72 75 63 74 6f 72 nt32, destructor
31d0: 20 3a 20 56 6f 69 64 2a 29 20 3a 20 49 6e 74 33 : Void*) : Int3
31e0: 32 0a 20 20 66 75 6e 20 73 74 65 70 20 3d 20 73 2. fun step = s
31f0: 71 6c 69 74 65 33 5f 73 74 65 70 28 73 74 61 74 qlite3_step(stat
3200: 65 6d 65 6e 74 20 3a 20 56 6f 69 64 2a 29 20 3a ement : Void*) :
3210: 20 49 6e 74 33 32 0a 20 20 66 75 6e 20 63 6f 6c Int32. fun col
3220: 75 6d 6e 5f 69 6e 74 36 34 20 3d 20 73 71 6c 69 umn_int64 = sqli
3230: 74 65 33 5f 63 6f 6c 75 6d 6e 5f 69 6e 74 36 34 te3_column_int64
3240: 28 73 74 61 74 65 6d 65 6e 74 20 3a 20 56 6f 69 (statement : Voi
3250: 64 2a 2c 20 63 6f 6c 75 6d 6e 20 3a 20 49 6e 74 d*, column : Int
3260: 33 32 29 20 3a 20 49 6e 74 36 34 0a 20 20 66 75 32) : Int64. fu
3270: 6e 20 63 6f 6c 75 6d 6e 5f 74 65 78 74 20 3d 20 n column_text =
3280: 73 71 6c 69 74 65 33 5f 63 6f 6c 75 6d 6e 5f 74 sqlite3_column_t
3290: 65 78 74 28 73 74 61 74 65 6d 65 6e 74 20 3a 20 ext(statement :
32a0: 56 6f 69 64 2a 2c 20 63 6f 6c 75 6d 6e 20 3a 20 Void*, column :
32b0: 49 6e 74 33 32 29 20 3a 20 55 49 6e 74 38 2a 0a Int32) : UInt8*.
32c0: 20 20 66 75 6e 20 63 6f 6c 75 6d 6e 5f 74 79 70 fun column_typ
32d0: 65 20 3d 20 73 71 6c 69 74 65 33 5f 63 6f 6c 75 e = sqlite3_colu
32e0: 6d 6e 5f 74 79 70 65 28 73 74 61 74 65 6d 65 6e mn_type(statemen
32f0: 74 20 3a 20 56 6f 69 64 2a 2c 20 63 6f 6c 75 6d t : Void*, colum
3300: 6e 20 3a 20 49 6e 74 33 32 29 20 3a 20 49 6e 74 n : Int32) : Int
3310: 33 32 0a 20 20 66 75 6e 20 66 69 6e 61 6c 69 7a 32. fun finaliz
3320: 65 20 3d 20 73 71 6c 69 74 65 33 5f 66 69 6e 61 e = sqlite3_fina
3330: 6c 69 7a 65 28 73 74 61 74 65 6d 65 6e 74 20 3a lize(statement :
3340: 20 56 6f 69 64 2a 29 20 3a 20 49 6e 74 33 32 0a Void*) : Int32.
3350: 20 20 66 75 6e 20 65 78 65 63 20 3d 20 73 71 6c fun exec = sql
3360: 69 74 65 33 5f 65 78 65 63 28 64 62 20 3a 20 56 ite3_exec(db : V
3370: 6f 69 64 2a 2c 20 73 71 6c 20 3a 20 55 49 6e 74 oid*, sql : UInt
3380: 38 2a 2c 20 63 61 6c 6c 62 61 63 6b 20 3a 20 56 8*, callback : V
3390: 6f 69 64 2a 2c 20 63 6f 6e 74 65 78 74 20 3a 20 oid*, context :
33a0: 56 6f 69 64 2a 2c 20 65 72 72 6f 72 20 3a 20 55 Void*, error : U
33b0: 49 6e 74 38 2a 2a 29 20 3a 20 49 6e 74 33 32 0a Int8**) : Int32.
33c0: 20 20 66 75 6e 20 62 75 73 79 5f 74 69 6d 65 6f fun busy_timeo
33d0: 75 74 20 3d 20 73 71 6c 69 74 65 33 5f 62 75 73 ut = sqlite3_bus
33e0: 79 5f 74 69 6d 65 6f 75 74 28 64 62 20 3a 20 56 y_timeout(db : V
33f0: 6f 69 64 2a 2c 20 6d 69 6c 6c 69 73 65 63 6f 6e oid*, millisecon
3400: 64 73 20 3a 20 49 6e 74 33 32 29 20 3a 20 49 6e ds : Int32) : In
3410: 74 33 32 0a 20 20 66 75 6e 20 65 78 74 65 6e 64 t32. fun extend
3420: 65 64 5f 65 72 72 63 6f 64 65 20 3d 20 73 71 6c ed_errcode = sql
3430: 69 74 65 33 5f 65 78 74 65 6e 64 65 64 5f 65 72 ite3_extended_er
3440: 72 63 6f 64 65 28 64 62 20 3a 20 56 6f 69 64 2a rcode(db : Void*
3450: 29 20 3a 20 49 6e 74 33 32 0a 20 20 66 75 6e 20 ) : Int32. fun
3460: 65 72 72 6d 73 67 20 3d 20 73 71 6c 69 74 65 33 errmsg = sqlite3
3470: 5f 65 72 72 6d 73 67 28 64 62 20 3a 20 56 6f 69 _errmsg(db : Voi
3480: 64 2a 29 20 3a 20 55 49 6e 74 38 2a 0a 65 6e 64 d*) : UInt8*.end
3490: 0a 0a 6d 6f 64 75 6c 65 20 52 65 63 65 69 70 74 ..module Receipt
34a0: 73 0a 20 20 63 6c 61 73 73 20 52 65 66 75 73 65 s. class Refuse
34b0: 64 20 3c 20 45 78 63 65 70 74 69 6f 6e 3b 20 65 d < Exception; e
34c0: 6e 64 0a 0a 20 20 63 6c 61 73 73 20 55 6e 61 76 nd.. class Unav
34d0: 61 69 6c 61 62 6c 65 20 3c 20 45 78 63 65 70 74 ailable < Except
34e0: 69 6f 6e 3b 20 65 6e 64 0a 0a 20 20 72 65 63 6f ion; end.. reco
34f0: 72 64 20 49 64 65 6e 74 69 74 79 2c 20 72 65 70 rd Identity, rep
3500: 6f 73 69 74 6f 72 79 20 3a 20 53 74 72 69 6e 67 ository : String
3510: 2c 20 63 61 70 61 62 69 6c 69 74 69 65 73 20 3a , capabilities :
3520: 20 53 74 72 69 6e 67 0a 20 20 72 65 63 6f 72 64 String. record
3530: 20 52 65 71 75 65 73 74 2c 20 61 72 74 69 66 61 Request, artifa
3540: 63 74 73 20 3a 20 41 72 72 61 79 28 53 74 72 69 cts : Array(Stri
3550: 6e 67 29 2c 20 63 61 70 61 62 69 6c 69 74 69 65 ng), capabilitie
3560: 73 5f 6f 66 20 3a 20 53 74 72 69 6e 67 3f 0a 0a s_of : String?..
3570: 20 20 72 65 63 6f 72 64 20 41 72 74 69 66 61 63 record Artifac
3580: 74 2c 20 61 72 74 69 66 61 63 74 20 3a 20 53 74 t, artifact : St
3590: 72 69 6e 67 2c 20 72 65 63 65 69 76 65 64 20 3a ring, received :
35a0: 20 42 6f 6f 6c 20 3d 20 66 61 6c 73 65 2c 20 73 Bool = false, s
35b0: 65 6e 64 65 72 20 3a 20 53 74 72 69 6e 67 3f 20 ender : String?
35c0: 3d 20 6e 69 6c 2c 0a 20 20 20 20 63 61 70 61 62 = nil,. capab
35d0: 69 6c 69 74 69 65 73 20 3a 20 53 74 72 69 6e 67 ilities : String
35e0: 3f 20 3d 20 6e 69 6c 2c 20 72 65 63 65 69 70 74 ? = nil, receipt
35f0: 20 3a 20 49 6e 74 36 34 3f 20 3d 20 6e 69 6c 2c : Int64? = nil,
3600: 20 72 69 64 20 3a 20 49 6e 74 36 34 3f 20 3d 20 rid : Int64? =
3610: 6e 69 6c 20 64 6f 0a 20 20 20 20 69 6e 63 6c 75 nil do. inclu
3620: 64 65 20 4a 53 4f 4e 3a 3a 53 65 72 69 61 6c 69 de JSON::Seriali
3630: 7a 61 62 6c 65 0a 20 20 65 6e 64 0a 0a 20 20 73 zable. end.. s
3640: 74 72 75 63 74 20 55 73 65 72 0a 20 20 20 20 69 truct User. i
3650: 6e 63 6c 75 64 65 20 4a 53 4f 4e 3a 3a 53 65 72 nclude JSON::Ser
3660: 69 61 6c 69 7a 61 62 6c 65 0a 20 20 20 20 67 65 ializable. ge
3670: 74 74 65 72 20 75 73 65 72 20 3a 20 53 74 72 69 tter user : Stri
3680: 6e 67 0a 20 20 20 20 40 5b 4a 53 4f 4e 3a 3a 46 ng. @[JSON::F
3690: 69 65 6c 64 28 65 6d 69 74 5f 6e 75 6c 6c 3a 20 ield(emit_null:
36a0: 74 72 75 65 29 5d 0a 20 20 20 20 67 65 74 74 65 true)]. gette
36b0: 72 20 63 61 70 61 62 69 6c 69 74 69 65 73 20 3a r capabilities :
36c0: 20 53 74 72 69 6e 67 3f 0a 20 20 20 20 67 65 74 String?. get
36d0: 74 65 72 20 77 69 6b 69 5f 6d 6f 64 65 72 61 74 ter wiki_moderat
36e0: 65 64 20 3a 20 42 6f 6f 6c 3f 0a 20 20 20 20 67 ed : Bool?. g
36f0: 65 74 74 65 72 20 74 69 63 6b 65 74 5f 6d 6f 64 etter ticket_mod
3700: 65 72 61 74 65 64 20 3a 20 42 6f 6f 6c 3f 0a 0a erated : Bool?..
3710: 20 20 20 20 64 65 66 20 69 6e 69 74 69 61 6c 69 def initiali
3720: 7a 65 28 40 75 73 65 72 2c 20 40 63 61 70 61 62 ze(@user, @capab
3730: 69 6c 69 74 69 65 73 20 3d 20 6e 69 6c 2c 20 40 ilities = nil, @
3740: 77 69 6b 69 5f 6d 6f 64 65 72 61 74 65 64 20 3d wiki_moderated =
3750: 20 6e 69 6c 2c 20 40 74 69 63 6b 65 74 5f 6d 6f nil, @ticket_mo
3760: 64 65 72 61 74 65 64 20 3d 20 6e 69 6c 29 3b 20 derated = nil);
3770: 65 6e 64 0a 20 20 65 6e 64 0a 0a 20 20 72 65 63 end. end.. rec
3780: 6f 72 64 20 41 6e 73 77 65 72 2c 20 61 72 74 69 ord Answer, arti
3790: 66 61 63 74 73 20 3a 20 41 72 72 61 79 28 41 72 facts : Array(Ar
37a0: 74 69 66 61 63 74 29 2c 20 75 73 65 72 20 3a 20 tifact), user :
37b0: 55 73 65 72 3f 20 3d 20 6e 69 6c 20 64 6f 0a 20 User? = nil do.
37c0: 20 20 20 69 6e 63 6c 75 64 65 20 4a 53 4f 4e 3a include JSON:
37d0: 3a 53 65 72 69 61 6c 69 7a 61 62 6c 65 0a 20 20 :Serializable.
37e0: 65 6e 64 0a 0a 20 20 64 65 66 20 73 65 6c 66 2e end.. def self.
37f0: 69 64 65 6e 74 69 74 79 28 6c 69 6e 65 20 3a 20 identity(line :
3800: 53 74 72 69 6e 67 2c 20 72 65 70 6f 73 69 74 6f String, reposito
3810: 72 69 65 73 20 3a 20 53 74 72 69 6e 67 29 20 3a ries : String) :
3820: 20 49 64 65 6e 74 69 74 79 0a 20 20 20 20 6f 62 Identity. ob
3830: 6a 65 63 74 20 3d 20 4a 53 4f 4e 2e 70 61 72 73 ject = JSON.pars
3840: 65 28 6c 69 6e 65 29 2e 61 73 5f 68 3f 0a 20 20 e(line).as_h?.
3850: 20 20 72 61 69 73 65 20 52 65 66 75 73 65 64 2e raise Refused.
3860: 6e 65 77 28 22 69 64 65 6e 74 69 74 79 20 6d 75 new("identity mu
3870: 73 74 20 62 65 20 61 6e 20 6f 62 6a 65 63 74 22 st be an object"
3880: 29 20 75 6e 6c 65 73 73 20 6f 62 6a 65 63 74 0a ) unless object.
3890: 20 20 20 20 6e 61 6d 65 20 3d 20 6f 62 6a 65 63 name = objec
38a0: 74 5b 22 72 65 70 6f 73 69 74 6f 72 79 22 5d 3f t["repository"]?
38b0: 2e 74 72 79 28 26 2e 61 73 5f 73 3f 29 0a 20 20 .try(&.as_s?).
38c0: 20 20 63 61 70 73 20 3d 20 6f 62 6a 65 63 74 5b caps = object[
38d0: 22 63 61 70 61 62 69 6c 69 74 69 65 73 22 5d 3f "capabilities"]?
38e0: 2e 74 72 79 28 26 2e 61 73 5f 73 3f 29 0a 20 20 .try(&.as_s?).
38f0: 20 20 69 66 20 75 73 65 72 20 3d 20 6f 62 6a 65 if user = obje
3900: 63 74 5b 22 75 73 65 72 22 5d 3f 0a 20 20 20 20 ct["user"]?.
3910: 20 20 72 61 69 73 65 20 52 65 66 75 73 65 64 2e raise Refused.
3920: 6e 65 77 28 22 69 6e 76 61 6c 69 64 20 75 73 65 new("invalid use
3930: 72 22 29 20 75 6e 6c 65 73 73 20 75 73 65 72 2e r") unless user.
3940: 72 61 77 2e 6e 69 6c 3f 20 7c 7c 20 75 73 65 72 raw.nil? || user
3950: 2e 61 73 5f 73 3f 0a 20 20 20 20 65 6e 64 0a 20 .as_s?. end.
3960: 20 20 20 72 61 69 73 65 20 52 65 66 75 73 65 64 raise Refused
3970: 2e 6e 65 77 28 22 69 6e 76 61 6c 69 64 20 63 61 .new("invalid ca
3980: 70 61 62 69 6c 69 74 69 65 73 22 29 20 75 6e 6c pabilities") unl
3990: 65 73 73 20 63 61 70 73 0a 20 20 20 20 72 61 69 ess caps. rai
39a0: 73 65 20 52 65 66 75 73 65 64 2e 6e 65 77 28 22 se Refused.new("
39b0: 69 6e 76 61 6c 69 64 20 72 65 70 6f 73 69 74 6f invalid reposito
39c0: 72 79 22 29 20 75 6e 6c 65 73 73 20 6e 61 6d 65 ry") unless name
39d0: 20 26 26 20 2f 5c 41 5b 41 2d 5a 61 2d 7a 30 2d && /\A[A-Za-z0-
39e0: 39 5f 5d 5b 41 2d 5a 61 2d 7a 30 2d 39 2e 5f 2d 9_][A-Za-z0-9._-
39f0: 5d 7b 30 2c 39 39 7d 5c 7a 2f 2e 6d 61 74 63 68 ]{0,99}\z/.match
3a00: 65 73 3f 28 6e 61 6d 65 29 20 26 26 20 21 6e 61 es?(name) && !na
3a10: 6d 65 2e 65 6e 64 73 5f 77 69 74 68 3f 28 22 2e me.ends_with?(".
3a20: 66 6f 73 73 69 6c 22 29 0a 20 20 20 20 72 61 69 fossil"). rai
3a30: 73 65 20 52 65 66 75 73 65 64 2e 6e 65 77 28 22 se Refused.new("
3a40: 72 65 70 6f 73 69 74 6f 72 79 20 69 73 20 6e 6f repository is no
3a50: 74 20 61 20 72 65 67 75 6c 61 72 20 66 69 6c 65 t a regular file
3a60: 22 29 20 75 6e 6c 65 73 73 20 46 69 6c 65 2e 66 ") unless File.f
3a70: 69 6c 65 3f 28 46 69 6c 65 2e 6a 6f 69 6e 28 72 ile?(File.join(r
3a80: 65 70 6f 73 69 74 6f 72 69 65 73 2c 20 6e 61 6d epositories, nam
3a90: 65 20 2b 20 22 2e 66 6f 73 73 69 6c 22 29 29 0a e + ".fossil")).
3aa0: 20 20 20 20 49 64 65 6e 74 69 74 79 2e 6e 65 77 Identity.new
3ab0: 28 6e 61 6d 65 2c 20 63 61 70 73 29 0a 20 20 65 (name, caps). e
3ac0: 6e 64 0a 0a 20 20 64 65 66 20 73 65 6c 66 2e 72 nd.. def self.r
3ad0: 65 71 75 65 73 74 28 6c 69 6e 65 20 3a 20 53 74 equest(line : St
3ae0: 72 69 6e 67 29 20 3a 20 52 65 71 75 65 73 74 0a ring) : Request.
3af0: 20 20 20 20 6f 62 6a 65 63 74 20 3d 20 4a 53 4f object = JSO
3b00: 4e 2e 70 61 72 73 65 28 6c 69 6e 65 29 2e 61 73 N.parse(line).as
3b10: 5f 68 3f 0a 20 20 20 20 72 61 69 73 65 20 52 65 _h?. raise Re
3b20: 66 75 73 65 64 2e 6e 65 77 28 22 72 65 71 75 65 fused.new("reque
3b30: 73 74 20 6d 75 73 74 20 62 65 20 61 6e 20 6f 62 st must be an ob
3b40: 6a 65 63 74 22 29 20 75 6e 6c 65 73 73 20 6f 62 ject") unless ob
3b50: 6a 65 63 74 0a 20 20 20 20 6f 62 6a 65 63 74 2e ject. object.
3b60: 65 61 63 68 5f 6b 65 79 20 7b 20 7c 6b 65 79 7c each_key { |key|
3b70: 20 72 61 69 73 65 20 52 65 66 75 73 65 64 2e 6e raise Refused.n
3b80: 65 77 28 22 75 6e 6b 6e 6f 77 6e 20 6b 65 79 3a ew("unknown key:
3b90: 20 23 7b 6b 65 79 7d 22 29 20 75 6e 6c 65 73 73 #{key}") unless
3ba0: 20 7b 22 6f 63 78 5f 72 65 63 65 69 70 74 22 2c {"ocx_receipt",
3bb0: 20 22 61 72 74 69 66 61 63 74 73 22 2c 20 22 63 "artifacts", "c
3bc0: 61 70 61 62 69 6c 69 74 69 65 73 5f 6f 66 22 7d apabilities_of"}
3bd0: 2e 69 6e 63 6c 75 64 65 73 3f 28 6b 65 79 29 20 .includes?(key)
3be0: 7d 0a 20 20 20 20 72 61 69 73 65 20 52 65 66 75 }. raise Refu
3bf0: 73 65 64 2e 6e 65 77 28 22 69 6e 76 61 6c 69 64 sed.new("invalid
3c00: 20 6f 63 78 5f 72 65 63 65 69 70 74 22 29 20 75 ocx_receipt") u
3c10: 6e 6c 65 73 73 20 6f 62 6a 65 63 74 5b 22 6f 63 nless object["oc
3c20: 78 5f 72 65 63 65 69 70 74 22 5d 3f 2e 74 72 79 x_receipt"]?.try
3c30: 28 26 2e 61 73 5f 69 36 34 3f 29 20 3d 3d 20 31 (&.as_i64?) == 1
3c40: 0a 20 20 20 20 68 61 73 68 65 73 20 3d 20 5b 5d . hashes = []
3c50: 20 6f 66 20 53 74 72 69 6e 67 0a 20 20 20 20 69 of String. i
3c60: 66 20 76 61 6c 75 65 20 3d 20 6f 62 6a 65 63 74 f value = object
3c70: 5b 22 61 72 74 69 66 61 63 74 73 22 5d 3f 0a 20 ["artifacts"]?.
3c80: 20 20 20 20 20 6c 69 73 74 20 3d 20 76 61 6c 75 list = valu
3c90: 65 2e 61 73 5f 61 3f 0a 20 20 20 20 20 20 72 61 e.as_a?. ra
3ca0: 69 73 65 20 52 65 66 75 73 65 64 2e 6e 65 77 28 ise Refused.new(
3cb0: 22 69 6e 76 61 6c 69 64 20 61 72 74 69 66 61 63 "invalid artifac
3cc0: 74 73 22 29 20 75 6e 6c 65 73 73 20 6c 69 73 74 ts") unless list
3cd0: 20 26 26 20 6c 69 73 74 2e 73 69 7a 65 20 3c 3d && list.size <=
3ce0: 20 36 34 0a 20 20 20 20 20 20 6c 69 73 74 2e 65 64. list.e
3cf0: 61 63 68 20 64 6f 20 7c 69 74 65 6d 7c 0a 20 20 ach do |item|.
3d00: 20 20 20 20 20 20 68 61 73 68 20 3d 20 69 74 65 hash = ite
3d10: 6d 2e 61 73 5f 73 3f 0a 20 20 20 20 20 20 20 20 m.as_s?.
3d20: 72 61 69 73 65 20 52 65 66 75 73 65 64 2e 6e 65 raise Refused.ne
3d30: 77 28 22 69 6e 76 61 6c 69 64 20 61 72 74 69 66 w("invalid artif
3d40: 61 63 74 22 29 20 75 6e 6c 65 73 73 20 68 61 73 act") unless has
3d50: 68 20 26 26 20 2f 5c 41 28 5b 30 2d 39 61 2d 66 h && /\A([0-9a-f
3d60: 5d 7b 34 30 7d 7c 5b 30 2d 39 61 2d 66 5d 7b 36 ]{40}|[0-9a-f]{6
3d70: 34 7d 29 5c 7a 2f 2e 6d 61 74 63 68 65 73 3f 28 4})\z/.matches?(
3d80: 68 61 73 68 29 0a 20 20 20 20 20 20 20 20 68 61 hash). ha
3d90: 73 68 65 73 20 3c 3c 20 68 61 73 68 0a 20 20 20 shes << hash.
3da0: 20 20 20 65 6e 64 0a 20 20 20 20 65 6e 64 0a 20 end. end.
3db0: 20 20 20 75 73 65 72 20 3d 20 6e 69 6c 0a 20 20 user = nil.
3dc0: 20 20 69 66 20 76 61 6c 75 65 20 3d 20 6f 62 6a if value = obj
3dd0: 65 63 74 5b 22 63 61 70 61 62 69 6c 69 74 69 65 ect["capabilitie
3de0: 73 5f 6f 66 22 5d 3f 0a 20 20 20 20 20 20 75 73 s_of"]?. us
3df0: 65 72 20 3d 20 76 61 6c 75 65 2e 61 73 5f 73 3f er = value.as_s?
3e00: 0a 20 20 20 20 20 20 72 61 69 73 65 20 52 65 66 . raise Ref
3e10: 75 73 65 64 2e 6e 65 77 28 22 69 6e 76 61 6c 69 used.new("invali
3e20: 64 20 63 61 70 61 62 69 6c 69 74 69 65 73 5f 6f d capabilities_o
3e30: 66 22 29 20 75 6e 6c 65 73 73 20 75 73 65 72 20 f") unless user
3e40: 26 26 20 75 73 65 72 2e 73 69 7a 65 20 3c 3d 20 && user.size <=
3e50: 31 30 30 20 26 26 20 75 73 65 72 2e 65 61 63 68 100 && user.each
3e60: 5f 63 68 61 72 2e 6e 6f 6e 65 3f 28 26 2e 63 6f _char.none?(&.co
3e70: 6e 74 72 6f 6c 3f 29 0a 20 20 20 20 65 6e 64 0a ntrol?). end.
3e80: 20 20 20 20 52 65 71 75 65 73 74 2e 6e 65 77 28 Request.new(
3e90: 68 61 73 68 65 73 2c 20 75 73 65 72 29 0a 20 20 hashes, user).
3ea0: 65 6e 64 0a 0a 20 20 63 6c 61 73 73 20 44 61 74 end.. class Dat
3eb0: 61 62 61 73 65 0a 20 20 20 20 41 52 54 49 46 41 abase. ARTIFA
3ec0: 43 54 20 20 20 20 20 20 20 20 3d 20 22 53 45 4c CT = "SEL
3ed0: 45 43 54 20 62 2e 72 69 64 2c 20 62 2e 72 63 76 ECT b.rid, b.rcv
3ee0: 69 64 2c 20 45 58 49 53 54 53 28 53 45 4c 45 43 id, EXISTS(SELEC
3ef0: 54 20 31 20 46 52 4f 4d 20 70 72 69 76 61 74 65 T 1 FROM private
3f00: 20 70 20 57 48 45 52 45 20 70 2e 72 69 64 3d 62 p WHERE p.rid=b
3f10: 2e 72 69 64 29 2c 20 75 2e 6c 6f 67 69 6e 2c 20 .rid), u.login,
3f20: 75 2e 63 61 70 20 46 52 4f 4d 20 62 6c 6f 62 20 u.cap FROM blob
3f30: 62 20 4c 45 46 54 20 4a 4f 49 4e 20 72 63 76 66 b LEFT JOIN rcvf
3f40: 72 6f 6d 20 72 20 4f 4e 20 72 2e 72 63 76 69 64 rom r ON r.rcvid
3f50: 3d 62 2e 72 63 76 69 64 20 4c 45 46 54 20 4a 4f =b.rcvid LEFT JO
3f60: 49 4e 20 75 73 65 72 20 75 20 4f 4e 20 75 2e 75 IN user u ON u.u
3f70: 69 64 3d 72 2e 75 69 64 20 57 48 45 52 45 20 62 id=r.uid WHERE b
3f80: 2e 75 75 69 64 3d 3f 31 20 41 4e 44 20 62 2e 73 .uuid=?1 AND b.s
3f90: 69 7a 65 3e 3d 30 22 0a 20 20 20 20 55 53 45 52 ize>=0". USER
3fa0: 20 20 20 20 20 20 20 20 20 20 20 20 3d 20 22 53 = "S
3fb0: 45 4c 45 43 54 20 63 61 70 20 46 52 4f 4d 20 75 ELECT cap FROM u
3fc0: 73 65 72 20 57 48 45 52 45 20 6c 6f 67 69 6e 3d ser WHERE login=
3fd0: 3f 31 22 0a 20 20 20 20 44 45 46 41 55 4c 54 53 ?1". DEFAULTS
3fe0: 20 20 20 20 20 20 20 20 3d 20 22 53 45 4c 45 43 = "SELEC
3ff0: 54 20 6c 6f 67 69 6e 2c 20 63 61 70 20 46 52 4f T login, cap FRO
4000: 4d 20 75 73 65 72 20 57 48 45 52 45 20 6c 6f 67 M user WHERE log
4010: 69 6e 20 49 4e 20 28 27 6e 6f 62 6f 64 79 27 2c in IN ('nobody',
4020: 27 61 6e 6f 6e 79 6d 6f 75 73 27 2c 27 72 65 61 'anonymous','rea
4030: 64 65 72 27 2c 27 64 65 76 65 6c 6f 70 65 72 27 der','developer'
4040: 29 22 0a 20 20 20 20 43 4f 4e 46 49 47 20 20 20 )". CONFIG
4050: 20 20 20 20 20 20 20 3d 20 22 53 45 4c 45 43 54 = "SELECT
4060: 20 76 61 6c 75 65 20 46 52 4f 4d 20 63 6f 6e 66 value FROM conf
4070: 69 67 20 57 48 45 52 45 20 6e 61 6d 65 3d 3f 31 ig WHERE name=?1
4080: 22 0a 20 20 20 20 45 52 52 4f 52 53 20 20 20 20 ". ERRORS
4090: 20 20 20 20 20 20 3d 20 25 77 28 4f 4b 20 45 52 = %w(OK ER
40a0: 52 4f 52 20 49 4e 54 45 52 4e 41 4c 20 50 45 52 ROR INTERNAL PER
40b0: 4d 20 41 42 4f 52 54 20 42 55 53 59 20 4c 4f 43 M ABORT BUSY LOC
40c0: 4b 45 44 20 4e 4f 4d 45 4d 20 52 45 41 44 4f 4e KED NOMEM READON
40d0: 4c 59 20 49 4e 54 45 52 52 55 50 54 20 49 4f 45 LY INTERRUPT IOE
40e0: 52 52 20 43 4f 52 52 55 50 54 20 4e 4f 54 46 4f RR CORRUPT NOTFO
40f0: 55 4e 44 20 46 55 4c 4c 20 43 41 4e 54 4f 50 45 UND FULL CANTOPE
4100: 4e 20 50 52 4f 54 4f 43 4f 4c 20 45 4d 50 54 59 N PROTOCOL EMPTY
4110: 20 53 43 48 45 4d 41 20 54 4f 4f 42 49 47 20 43 SCHEMA TOOBIG C
4120: 4f 4e 53 54 52 41 49 4e 54 20 4d 49 53 4d 41 54 ONSTRAINT MISMAT
4130: 43 48 20 4d 49 53 55 53 45 20 4e 4f 4c 46 53 20 CH MISUSE NOLFS
4140: 41 55 54 48 20 46 4f 52 4d 41 54 20 52 41 4e 47 AUTH FORMAT RANG
4150: 45 20 4e 4f 54 41 44 42 20 4e 4f 54 49 43 45 20 E NOTADB NOTICE
4160: 57 41 52 4e 49 4e 47 29 0a 20 20 20 20 52 45 41 WARNING). REA
4170: 44 4f 4e 4c 59 5f 45 52 52 4f 52 53 20 3d 20 25 DONLY_ERRORS = %
4180: 77 28 52 45 43 4f 56 45 52 59 20 43 41 4e 54 4c w(RECOVERY CANTL
4190: 4f 43 4b 20 52 4f 4c 4c 42 41 43 4b 20 44 42 4d OCK ROLLBACK DBM
41a0: 4f 56 45 44 20 43 41 4e 54 49 4e 49 54 20 44 49 OVED CANTINIT DI
41b0: 52 45 43 54 4f 52 59 29 0a 20 20 20 20 40 64 62 RECTORY). @db
41c0: 20 3d 20 50 6f 69 6e 74 65 72 28 56 6f 69 64 29 = Pointer(Void)
41d0: 2e 6e 75 6c 6c 0a 0a 20 20 20 20 64 65 66 20 69 .null.. def i
41e0: 6e 69 74 69 61 6c 69 7a 65 28 70 61 74 68 20 3a nitialize(path :
41f0: 20 53 74 72 69 6e 67 29 0a 20 20 20 20 20 20 63 String). c
4200: 68 65 63 6b 20 4c 69 62 53 51 4c 69 74 65 33 2e heck LibSQLite3.
4210: 6f 70 65 6e 5f 76 32 28 22 66 69 6c 65 3a 22 20 open_v2("file:"
4220: 2b 20 55 52 49 2e 65 6e 63 6f 64 65 5f 70 61 74 + URI.encode_pat
4230: 68 28 70 61 74 68 29 20 2b 20 22 3f 6d 6f 64 65 h(path) + "?mode
4240: 3d 72 6f 22 2c 20 70 6f 69 6e 74 65 72 6f 66 28 =ro", pointerof(
4250: 40 64 62 29 2c 20 31 20 7c 20 36 34 2c 20 6e 69 @db), 1 | 64, ni
4260: 6c 29 0a 20 20 20 20 20 20 65 78 65 63 75 74 65 l). execute
4270: 20 22 50 52 41 47 4d 41 20 71 75 65 72 79 5f 6f "PRAGMA query_o
4280: 6e 6c 79 3d 31 22 0a 20 20 20 20 20 20 63 68 65 nly=1". che
4290: 63 6b 20 4c 69 62 53 51 4c 69 74 65 33 2e 62 75 ck LibSQLite3.bu
42a0: 73 79 5f 74 69 6d 65 6f 75 74 28 40 64 62 2c 20 sy_timeout(@db,
42b0: 30 29 0a 20 20 20 20 20 20 65 78 65 63 75 74 65 0). execute
42c0: 20 22 42 45 47 49 4e 22 0a 20 20 20 20 72 65 73 "BEGIN". res
42d0: 63 75 65 20 65 78 0a 20 20 20 20 20 20 63 6c 6f cue ex. clo
42e0: 73 65 0a 20 20 20 20 20 20 72 61 69 73 65 20 65 se. raise e
42f0: 78 0a 20 20 20 20 65 6e 64 0a 0a 20 20 20 20 64 x. end.. d
4300: 65 66 20 63 68 65 63 6b 28 63 6f 64 65 20 3a 20 ef check(code :
4310: 49 6e 74 33 32 29 20 3a 20 4e 69 6c 0a 20 20 20 Int32) : Nil.
4320: 20 20 20 72 65 74 75 72 6e 20 69 66 20 63 6f 64 return if cod
4330: 65 20 3d 3d 20 30 0a 20 20 20 20 20 20 65 78 74 e == 0. ext
4340: 65 6e 64 65 64 20 3d 20 4c 69 62 53 51 4c 69 74 ended = LibSQLit
4350: 65 33 2e 65 78 74 65 6e 64 65 64 5f 65 72 72 63 e3.extended_errc
4360: 6f 64 65 28 40 64 62 29 0a 20 20 20 20 20 20 62 ode(@db). b
4370: 61 73 65 20 3d 20 65 78 74 65 6e 64 65 64 20 26 ase = extended &
4380: 20 32 35 35 0a 20 20 20 20 20 20 6e 61 6d 65 20 255. name
4390: 3d 20 22 53 51 4c 49 54 45 5f 22 20 2b 20 28 45 = "SQLITE_" + (E
43a0: 52 52 4f 52 53 5b 62 61 73 65 5d 3f 20 7c 7c 20 RRORS[base]? ||
43b0: 22 45 52 52 4f 52 22 29 0a 20 20 20 20 20 20 69 "ERROR"). i
43c0: 66 20 62 61 73 65 20 3d 3d 20 38 20 26 26 20 28 f base == 8 && (
43d0: 73 75 66 66 69 78 20 3d 20 52 45 41 44 4f 4e 4c suffix = READONL
43e0: 59 5f 45 52 52 4f 52 53 5b 28 65 78 74 65 6e 64 Y_ERRORS[(extend
43f0: 65 64 20 3e 3e 20 38 29 20 2d 20 31 5d 3f 29 20 ed >> 8) - 1]?)
4400: 26 26 20 65 78 74 65 6e 64 65 64 20 3e 20 32 35 && extended > 25
4410: 35 0a 20 20 20 20 20 20 20 20 6e 61 6d 65 20 2b 5. name +
4420: 3d 20 22 5f 22 20 2b 20 73 75 66 66 69 78 0a 20 = "_" + suffix.
4430: 20 20 20 20 20 65 6e 64 0a 20 20 20 20 20 20 72 end. r
4440: 61 69 73 65 20 55 6e 61 76 61 69 6c 61 62 6c 65 aise Unavailable
4450: 2e 6e 65 77 28 22 23 7b 6e 61 6d 65 7d 20 28 23 .new("#{name} (#
4460: 7b 65 78 74 65 6e 64 65 64 7d 29 3a 20 23 7b 53 {extended}): #{S
4470: 74 72 69 6e 67 2e 6e 65 77 28 4c 69 62 53 51 4c tring.new(LibSQL
4480: 69 74 65 33 2e 65 72 72 6d 73 67 28 40 64 62 29 ite3.errmsg(@db)
4490: 29 7d 22 29 0a 20 20 20 20 65 6e 64 0a 0a 20 20 )}"). end..
44a0: 20 20 64 65 66 20 65 78 65 63 75 74 65 28 73 71 def execute(sq
44b0: 6c 20 3a 20 53 74 72 69 6e 67 29 20 3a 20 4e 69 l : String) : Ni
44c0: 6c 0a 20 20 20 20 20 20 63 68 65 63 6b 20 4c 69 l. check Li
44d0: 62 53 51 4c 69 74 65 33 2e 65 78 65 63 28 40 64 bSQLite3.exec(@d
44e0: 62 2c 20 73 71 6c 2c 20 6e 69 6c 2c 20 6e 69 6c b, sql, nil, nil
44f0: 2c 20 6e 69 6c 29 0a 20 20 20 20 65 6e 64 0a 0a , nil). end..
4500: 20 20 20 20 64 65 66 20 72 6f 77 73 28 73 71 6c def rows(sql
4510: 20 3a 20 53 74 72 69 6e 67 2c 20 76 61 6c 75 65 : String, value
4520: 20 3a 20 53 74 72 69 6e 67 3f 20 3d 20 6e 69 6c : String? = nil
4530: 2c 20 26 62 6c 6f 63 6b 20 3a 20 50 6f 69 6e 74 , &block : Point
4540: 65 72 28 56 6f 69 64 29 20 2d 3e 29 20 3a 20 4e er(Void) ->) : N
4550: 69 6c 0a 20 20 20 20 20 20 73 74 61 74 65 6d 65 il. stateme
4560: 6e 74 20 3d 20 50 6f 69 6e 74 65 72 28 56 6f 69 nt = Pointer(Voi
4570: 64 29 2e 6e 75 6c 6c 0a 20 20 20 20 20 20 62 65 d).null. be
4580: 67 69 6e 0a 20 20 20 20 20 20 20 20 63 68 65 63 gin. chec
4590: 6b 20 4c 69 62 53 51 4c 69 74 65 33 2e 70 72 65 k LibSQLite3.pre
45a0: 70 61 72 65 5f 76 32 28 40 64 62 2c 20 73 71 6c pare_v2(@db, sql
45b0: 2c 20 2d 31 2c 20 70 6f 69 6e 74 65 72 6f 66 28 , -1, pointerof(
45c0: 73 74 61 74 65 6d 65 6e 74 29 2c 20 6e 69 6c 29 statement), nil)
45d0: 0a 20 20 20 20 20 20 20 20 63 68 65 63 6b 20 4c . check L
45e0: 69 62 53 51 4c 69 74 65 33 2e 62 69 6e 64 5f 74 ibSQLite3.bind_t
45f0: 65 78 74 28 73 74 61 74 65 6d 65 6e 74 2c 20 31 ext(statement, 1
4600: 2c 20 76 61 6c 75 65 2c 20 76 61 6c 75 65 2e 62 , value, value.b
4610: 79 74 65 73 69 7a 65 2c 20 50 6f 69 6e 74 65 72 ytesize, Pointer
4620: 28 56 6f 69 64 29 2e 6e 65 77 28 55 49 6e 74 36 (Void).new(UInt6
4630: 34 3a 3a 4d 41 58 29 29 20 69 66 20 76 61 6c 75 4::MAX)) if valu
4640: 65 0a 20 20 20 20 20 20 20 20 6c 6f 6f 70 20 64 e. loop d
4650: 6f 0a 20 20 20 20 20 20 20 20 20 20 63 6f 64 65 o. code
4660: 20 3d 20 4c 69 62 53 51 4c 69 74 65 33 2e 73 74 = LibSQLite3.st
4670: 65 70 28 73 74 61 74 65 6d 65 6e 74 29 0a 20 20 ep(statement).
4680: 20 20 20 20 20 20 20 20 62 72 65 61 6b 20 69 66 break if
4690: 20 63 6f 64 65 20 3d 3d 20 31 30 31 0a 20 20 20 code == 101.
46a0: 20 20 20 20 20 20 20 63 68 65 63 6b 20 63 6f 64 check cod
46b0: 65 20 75 6e 6c 65 73 73 20 63 6f 64 65 20 3d 3d e unless code ==
46c0: 20 31 30 30 0a 20 20 20 20 20 20 20 20 20 20 79 100. y
46d0: 69 65 6c 64 20 73 74 61 74 65 6d 65 6e 74 0a 20 ield statement.
46e0: 20 20 20 20 20 20 20 65 6e 64 0a 20 20 20 20 20 end.
46f0: 20 65 6e 73 75 72 65 0a 20 20 20 20 20 20 20 20 ensure.
4700: 4c 69 62 53 51 4c 69 74 65 33 2e 66 69 6e 61 6c LibSQLite3.final
4710: 69 7a 65 28 73 74 61 74 65 6d 65 6e 74 29 20 75 ize(statement) u
4720: 6e 6c 65 73 73 20 73 74 61 74 65 6d 65 6e 74 2e nless statement.
4730: 6e 75 6c 6c 3f 0a 20 20 20 20 20 20 65 6e 64 0a null?. end.
4740: 20 20 20 20 65 6e 64 0a 0a 20 20 20 20 64 65 66 end.. def
4750: 20 74 65 78 74 28 73 74 61 74 65 6d 65 6e 74 20 text(statement
4760: 3a 20 50 6f 69 6e 74 65 72 28 56 6f 69 64 29 2c : Pointer(Void),
4770: 20 63 6f 6c 75 6d 6e 20 3a 20 49 6e 74 33 32 29 column : Int32)
4780: 20 3a 20 53 74 72 69 6e 67 3f 0a 20 20 20 20 20 : String?.
4790: 20 53 74 72 69 6e 67 2e 6e 65 77 28 4c 69 62 53 String.new(LibS
47a0: 51 4c 69 74 65 33 2e 63 6f 6c 75 6d 6e 5f 74 65 QLite3.column_te
47b0: 78 74 28 73 74 61 74 65 6d 65 6e 74 2c 20 63 6f xt(statement, co
47c0: 6c 75 6d 6e 29 29 20 75 6e 6c 65 73 73 20 4c 69 lumn)) unless Li
47d0: 62 53 51 4c 69 74 65 33 2e 63 6f 6c 75 6d 6e 5f bSQLite3.column_
47e0: 74 79 70 65 28 73 74 61 74 65 6d 65 6e 74 2c 20 type(statement,
47f0: 63 6f 6c 75 6d 6e 29 20 3d 3d 20 35 0a 20 20 20 column) == 5.
4800: 20 65 6e 64 0a 0a 20 20 20 20 64 65 66 20 65 78 end.. def ex
4810: 70 61 6e 64 65 64 28 63 61 70 20 3a 20 53 74 72 panded(cap : Str
4820: 69 6e 67 2c 20 64 65 66 61 75 6c 74 73 20 3a 20 ing, defaults :
4830: 48 61 73 68 28 53 74 72 69 6e 67 2c 20 53 74 72 Hash(String, Str
4840: 69 6e 67 29 29 20 3a 20 53 74 72 69 6e 67 0a 20 ing)) : String.
4850: 20 20 20 20 20 63 61 70 73 20 3d 20 53 65 74 28 caps = Set(
4860: 43 68 61 72 29 2e 6e 65 77 0a 20 20 20 20 20 20 Char).new.
4870: 61 64 64 20 3d 20 2d 3e 28 76 61 6c 75 65 20 3a add = ->(value :
4880: 20 53 74 72 69 6e 67 29 20 7b 20 76 61 6c 75 65 String) { value
4890: 2e 65 61 63 68 5f 63 68 61 72 20 7b 20 7c 63 68 .each_char { |ch
48a0: 61 72 7c 20 63 61 70 73 20 3c 3c 20 63 68 61 72 ar| caps << char
48b0: 20 69 66 20 27 30 27 20 3c 3d 20 63 68 61 72 20 if '0' <= char
48c0: 3c 3d 20 27 7a 27 20 7d 20 7d 0a 20 20 20 20 20 <= 'z' } }.
48d0: 20 61 64 64 2e 63 61 6c 6c 28 63 61 70 29 0a 20 add.call(cap).
48e0: 20 20 20 20 20 61 64 64 2e 63 61 6c 6c 28 64 65 add.call(de
48f0: 66 61 75 6c 74 73 5b 22 61 6e 6f 6e 79 6d 6f 75 faults["anonymou
4900: 73 22 5d 3f 20 7c 7c 20 22 22 29 0a 20 20 20 20 s"]? || "").
4910: 20 20 61 64 64 2e 63 61 6c 6c 28 64 65 66 61 75 add.call(defau
4920: 6c 74 73 5b 22 6e 6f 62 6f 64 79 22 5d 3f 20 7c lts["nobody"]? |
4930: 7c 20 22 22 29 0a 20 20 20 20 20 20 61 64 64 2e | ""). add.
4940: 63 61 6c 6c 28 22 62 63 64 65 66 67 68 69 6a 6b call("bcdefghijk
4950: 6c 6d 6e 6f 70 71 72 74 77 7a 32 33 34 35 36 37 lmnopqrtwz234567
4960: 41 44 22 29 20 69 66 20 63 61 70 73 2e 69 6e 63 AD") if caps.inc
4970: 6c 75 64 65 73 3f 28 27 61 27 29 20 7c 7c 20 63 ludes?('a') || c
4980: 61 70 73 2e 69 6e 63 6c 75 64 65 73 3f 28 27 73 aps.includes?('s
4990: 27 29 0a 20 20 20 20 20 20 64 65 76 65 6c 6f 70 '). develop
49a0: 65 72 20 3d 20 63 61 70 73 2e 69 6e 63 6c 75 64 er = caps.includ
49b0: 65 73 3f 28 27 76 27 29 0a 20 20 20 20 20 20 61 es?('v'). a
49c0: 64 64 2e 63 61 6c 6c 28 64 65 66 61 75 6c 74 73 dd.call(defaults
49d0: 5b 22 64 65 76 65 6c 6f 70 65 72 22 5d 3f 20 7c ["developer"]? |
49e0: 7c 20 22 22 29 20 69 66 20 64 65 76 65 6c 6f 70 | "") if develop
49f0: 65 72 0a 20 20 20 20 20 20 69 66 20 63 61 70 73 er. if caps
4a00: 2e 69 6e 63 6c 75 64 65 73 3f 28 27 75 27 29 0a .includes?('u').
4a10: 20 20 20 20 20 20 20 20 61 64 64 2e 63 61 6c 6c add.call
4a20: 28 64 65 66 61 75 6c 74 73 5b 22 72 65 61 64 65 (defaults["reade
4a30: 72 22 5d 3f 20 7c 7c 20 22 22 29 0a 20 20 20 20 r"]? || "").
4a40: 20 20 20 20 61 64 64 2e 63 61 6c 6c 28 64 65 66 add.call(def
4a50: 61 75 6c 74 73 5b 22 64 65 76 65 6c 6f 70 65 72 aults["developer
4a60: 22 5d 3f 20 7c 7c 20 22 22 29 20 69 66 20 63 61 "]? || "") if ca
4a70: 70 73 2e 69 6e 63 6c 75 64 65 73 3f 28 27 76 27 ps.includes?('v'
4a80: 29 20 26 26 20 21 64 65 76 65 6c 6f 70 65 72 0a ) && !developer.
4a90: 20 20 20 20 20 20 65 6e 64 0a 20 20 20 20 20 20 end.
4aa0: 28 28 27 61 27 2e 2e 27 7a 27 29 2e 74 6f 5f 61 (('a'..'z').to_a
4ab0: 20 2b 20 28 27 30 27 2e 2e 27 39 27 29 2e 74 6f + ('0'..'9').to
4ac0: 5f 61 20 2b 20 28 27 41 27 2e 2e 27 5a 27 29 2e _a + ('A'..'Z').
4ad0: 74 6f 5f 61 29 2e 73 65 6c 65 63 74 20 7b 20 7c to_a).select { |
4ae0: 63 68 61 72 7c 20 63 61 70 73 2e 69 6e 63 6c 75 char| caps.inclu
4af0: 64 65 73 3f 28 63 68 61 72 29 20 7d 2e 6a 6f 69 des?(char) }.joi
4b00: 6e 0a 20 20 20 20 65 6e 64 0a 0a 20 20 20 20 64 n. end.. d
4b10: 65 66 20 6d 6f 64 65 72 61 74 65 64 28 6e 61 6d ef moderated(nam
4b20: 65 20 3a 20 53 74 72 69 6e 67 2c 20 63 61 70 73 e : String, caps
4b30: 20 3a 20 53 74 72 69 6e 67 2c 20 70 65 72 6d 69 : String, permi
4b40: 73 73 69 6f 6e 20 3a 20 43 68 61 72 29 20 3a 20 ssion : Char) :
4b50: 42 6f 6f 6c 0a 20 20 20 20 20 20 74 72 75 74 68 Bool. truth
4b60: 20 3d 20 66 61 6c 73 65 0a 20 20 20 20 20 20 72 = false. r
4b70: 6f 77 73 28 43 4f 4e 46 49 47 2c 20 6e 61 6d 65 ows(CONFIG, name
4b80: 29 20 7b 20 7c 72 6f 77 7c 20 74 72 75 74 68 20 ) { |row| truth
4b90: 3d 20 7b 22 6f 6e 22 2c 20 22 79 65 73 22 2c 20 = {"on", "yes",
4ba0: 22 74 72 75 65 22 2c 20 22 31 22 7d 2e 69 6e 63 "true", "1"}.inc
4bb0: 6c 75 64 65 73 3f 28 28 74 65 78 74 28 72 6f 77 ludes?((text(row
4bc0: 2c 20 30 29 20 7c 7c 20 22 22 29 2e 64 6f 77 6e , 0) || "").down
4bd0: 63 61 73 65 29 20 7d 0a 20 20 20 20 20 20 74 72 case) }. tr
4be0: 75 74 68 20 26 26 20 21 63 61 70 73 2e 69 6e 63 uth && !caps.inc
4bf0: 6c 75 64 65 73 3f 28 70 65 72 6d 69 73 73 69 6f ludes?(permissio
4c00: 6e 29 0a 20 20 20 20 65 6e 64 0a 0a 20 20 20 20 n). end..
4c10: 64 65 66 20 61 6e 73 77 65 72 28 69 64 65 6e 74 def answer(ident
4c20: 69 74 79 20 3a 20 49 64 65 6e 74 69 74 79 2c 20 ity : Identity,
4c30: 72 65 71 75 65 73 74 20 3a 20 52 65 71 75 65 73 request : Reques
4c40: 74 29 20 3a 20 41 6e 73 77 65 72 0a 20 20 20 20 t) : Answer.
4c50: 20 20 64 65 66 61 75 6c 74 73 20 3d 20 7b 7d 20 defaults = {}
4c60: 6f 66 20 53 74 72 69 6e 67 20 3d 3e 20 53 74 72 of String => Str
4c70: 69 6e 67 0a 20 20 20 20 20 20 72 6f 77 73 28 44 ing. rows(D
4c80: 45 46 41 55 4c 54 53 29 20 7b 20 7c 72 6f 77 7c EFAULTS) { |row|
4c90: 20 64 65 66 61 75 6c 74 73 5b 74 65 78 74 28 72 defaults[text(r
4ca0: 6f 77 2c 20 30 29 2e 6e 6f 74 5f 6e 69 6c 21 5d ow, 0).not_nil!]
4cb0: 20 3d 20 74 65 78 74 28 72 6f 77 2c 20 31 29 20 = text(row, 1)
4cc0: 7c 7c 20 22 22 20 7d 0a 20 20 20 20 20 20 61 72 || "" }. ar
4cd0: 74 69 66 61 63 74 73 20 3d 20 72 65 71 75 65 73 tifacts = reques
4ce0: 74 2e 61 72 74 69 66 61 63 74 73 2e 6d 61 70 20 t.artifacts.map
4cf0: 64 6f 20 7c 68 61 73 68 7c 0a 20 20 20 20 20 20 do |hash|.
4d00: 20 20 61 72 74 69 66 61 63 74 20 3d 20 41 72 74 artifact = Art
4d10: 69 66 61 63 74 2e 6e 65 77 28 68 61 73 68 29 0a ifact.new(hash).
4d20: 20 20 20 20 20 20 20 20 72 6f 77 73 28 41 52 54 rows(ART
4d30: 49 46 41 43 54 2c 20 68 61 73 68 29 20 64 6f 20 IFACT, hash) do
4d40: 7c 72 6f 77 7c 0a 20 20 20 20 20 20 20 20 20 20 |row|.
4d50: 6e 65 78 74 20 69 66 20 4c 69 62 53 51 4c 69 74 next if LibSQLit
4d60: 65 33 2e 63 6f 6c 75 6d 6e 5f 69 6e 74 36 34 28 e3.column_int64(
4d70: 72 6f 77 2c 20 32 29 20 21 3d 20 30 20 26 26 20 row, 2) != 0 &&
4d80: 21 69 64 65 6e 74 69 74 79 2e 63 61 70 61 62 69 !identity.capabi
4d90: 6c 69 74 69 65 73 2e 69 6e 63 6c 75 64 65 73 3f lities.includes?
4da0: 28 27 78 27 29 0a 20 20 20 20 20 20 20 20 20 20 ('x').
4db0: 73 65 6e 64 65 72 20 3d 20 74 65 78 74 28 72 6f sender = text(ro
4dc0: 77 2c 20 33 29 0a 20 20 20 20 20 20 20 20 20 20 w, 3).
4dd0: 61 72 74 69 66 61 63 74 20 3d 20 41 72 74 69 66 artifact = Artif
4de0: 61 63 74 2e 6e 65 77 28 61 72 74 69 66 61 63 74 act.new(artifact
4df0: 3a 20 68 61 73 68 2c 20 72 65 63 65 69 76 65 64 : hash, received
4e00: 3a 20 74 72 75 65 2c 20 73 65 6e 64 65 72 3a 20 : true, sender:
4e10: 73 65 6e 64 65 72 2c 0a 20 20 20 20 20 20 20 20 sender,.
4e20: 20 20 20 20 63 61 70 61 62 69 6c 69 74 69 65 73 capabilities
4e30: 3a 20 73 65 6e 64 65 72 20 3f 20 65 78 70 61 6e : sender ? expan
4e40: 64 65 64 28 74 65 78 74 28 72 6f 77 2c 20 34 29 ded(text(row, 4)
4e50: 20 7c 7c 20 22 22 2c 20 64 65 66 61 75 6c 74 73 || "", defaults
4e60: 29 20 3a 20 6e 69 6c 2c 0a 20 20 20 20 20 20 20 ) : nil,.
4e70: 20 20 20 20 20 72 65 63 65 69 70 74 3a 20 4c 69 receipt: Li
4e80: 62 53 51 4c 69 74 65 33 2e 63 6f 6c 75 6d 6e 5f bSQLite3.column_
4e90: 69 6e 74 36 34 28 72 6f 77 2c 20 31 29 2c 20 72 int64(row, 1), r
4ea0: 69 64 3a 20 4c 69 62 53 51 4c 69 74 65 33 2e 63 id: LibSQLite3.c
4eb0: 6f 6c 75 6d 6e 5f 69 6e 74 36 34 28 72 6f 77 2c olumn_int64(row,
4ec0: 20 30 29 29 0a 20 20 20 20 20 20 20 20 65 6e 64 0)). end
4ed0: 0a 20 20 20 20 20 20 20 20 61 72 74 69 66 61 63 . artifac
4ee0: 74 0a 20 20 20 20 20 20 65 6e 64 0a 20 20 20 20 t. end.
4ef0: 20 20 75 73 65 72 20 3d 20 6e 69 6c 0a 20 20 20 user = nil.
4f00: 20 20 20 69 66 20 6e 61 6d 65 20 3d 20 72 65 71 if name = req
4f10: 75 65 73 74 2e 63 61 70 61 62 69 6c 69 74 69 65 uest.capabilitie
4f20: 73 5f 6f 66 0a 20 20 20 20 20 20 20 20 75 73 65 s_of. use
4f30: 72 20 3d 20 55 73 65 72 2e 6e 65 77 28 6e 61 6d r = User.new(nam
4f40: 65 29 0a 20 20 20 20 20 20 20 20 72 6f 77 73 28 e). rows(
4f50: 55 53 45 52 2c 20 6e 61 6d 65 29 20 64 6f 20 7c USER, name) do |
4f60: 72 6f 77 7c 0a 20 20 20 20 20 20 20 20 20 20 63 row|. c
4f70: 61 70 73 20 3d 20 65 78 70 61 6e 64 65 64 28 74 aps = expanded(t
4f80: 65 78 74 28 72 6f 77 2c 20 30 29 20 7c 7c 20 22 ext(row, 0) || "
4f90: 22 2c 20 64 65 66 61 75 6c 74 73 29 0a 20 20 20 ", defaults).
4fa0: 20 20 20 20 20 20 20 75 73 65 72 20 3d 20 55 73 user = Us
4fb0: 65 72 2e 6e 65 77 28 6e 61 6d 65 2c 20 63 61 70 er.new(name, cap
4fc0: 73 2c 20 6d 6f 64 65 72 61 74 65 64 28 22 6d 6f s, moderated("mo
4fd0: 64 72 65 71 2d 77 69 6b 69 22 2c 20 63 61 70 73 dreq-wiki", caps
4fe0: 2c 20 27 6c 27 29 2c 20 6d 6f 64 65 72 61 74 65 , 'l'), moderate
4ff0: 64 28 22 6d 6f 64 72 65 71 2d 74 6b 74 22 2c 20 d("modreq-tkt",
5000: 63 61 70 73 2c 20 27 71 27 29 29 0a 20 20 20 20 caps, 'q')).
5010: 20 20 20 20 65 6e 64 0a 20 20 20 20 20 20 65 6e end. en
5020: 64 0a 20 20 20 20 20 20 65 78 65 63 75 74 65 20 d. execute
5030: 22 43 4f 4d 4d 49 54 22 0a 20 20 20 20 20 20 41 "COMMIT". A
5040: 6e 73 77 65 72 2e 6e 65 77 28 61 72 74 69 66 61 nswer.new(artifa
5050: 63 74 73 2c 20 75 73 65 72 29 0a 20 20 20 20 65 cts, user). e
5060: 6e 64 0a 0a 20 20 20 20 64 65 66 20 63 6c 6f 73 nd.. def clos
5070: 65 20 3a 20 4e 69 6c 0a 20 20 20 20 20 20 4c 69 e : Nil. Li
5080: 62 53 51 4c 69 74 65 33 2e 63 6c 6f 73 65 5f 76 bSQLite3.close_v
5090: 32 28 40 64 62 29 20 75 6e 6c 65 73 73 20 40 64 2(@db) unless @d
50a0: 62 2e 6e 75 6c 6c 3f 0a 20 20 20 20 20 20 40 64 b.null?. @d
50b0: 62 20 3d 20 50 6f 69 6e 74 65 72 28 56 6f 69 64 b = Pointer(Void
50c0: 29 2e 6e 75 6c 6c 0a 20 20 20 20 65 6e 64 0a 20 ).null. end.
50d0: 20 65 6e 64 0a 0a 20 20 64 65 66 20 73 65 6c 66 end.. def self
50e0: 2e 6c 69 6e 65 28 73 6f 63 6b 65 74 20 3a 20 55 .line(socket : U
50f0: 4e 49 58 53 6f 63 6b 65 74 2c 20 6c 69 6d 69 74 NIXSocket, limit
5100: 20 3a 20 49 6e 74 33 32 2c 20 64 65 61 64 6c 69 : Int32, deadli
5110: 6e 65 20 3a 20 54 69 6d 65 29 20 3a 20 53 74 72 ne : Time) : Str
5120: 69 6e 67 0a 20 20 20 20 62 79 74 65 73 20 3d 20 ing. bytes =
5130: 49 4f 3a 3a 4d 65 6d 6f 72 79 2e 6e 65 77 0a 20 IO::Memory.new.
5140: 20 20 20 6c 6f 6f 70 20 64 6f 0a 20 20 20 20 20 loop do.
5150: 20 72 65 6d 61 69 6e 69 6e 67 20 3d 20 64 65 61 remaining = dea
5160: 64 6c 69 6e 65 20 2d 20 54 69 6d 65 2e 75 74 63 dline - Time.utc
5170: 0a 20 20 20 20 20 20 72 61 69 73 65 20 49 4f 3a . raise IO:
5180: 3a 54 69 6d 65 6f 75 74 45 72 72 6f 72 2e 6e 65 :TimeoutError.ne
5190: 77 28 22 64 65 61 64 6c 69 6e 65 22 29 20 69 66 w("deadline") if
51a0: 20 72 65 6d 61 69 6e 69 6e 67 20 3c 3d 20 54 69 remaining <= Ti
51b0: 6d 65 3a 3a 53 70 61 6e 2e 7a 65 72 6f 0a 20 20 me::Span.zero.
51c0: 20 20 20 20 73 6f 63 6b 65 74 2e 72 65 61 64 5f socket.read_
51d0: 74 69 6d 65 6f 75 74 20 3d 20 72 65 6d 61 69 6e timeout = remain
51e0: 69 6e 67 0a 20 20 20 20 20 20 62 79 74 65 20 3d ing. byte =
51f0: 20 73 6f 63 6b 65 74 2e 72 65 61 64 5f 62 79 74 socket.read_byt
5200: 65 0a 20 20 20 20 20 20 72 61 69 73 65 20 52 65 e. raise Re
5210: 66 75 73 65 64 2e 6e 65 77 28 22 65 78 70 65 63 fused.new("expec
5220: 74 65 64 20 74 77 6f 20 4a 53 4f 4e 20 6c 69 6e ted two JSON lin
5230: 65 73 22 29 20 75 6e 6c 65 73 73 20 62 79 74 65 es") unless byte
5240: 0a 20 20 20 20 20 20 62 72 65 61 6b 20 69 66 20 . break if
5250: 62 79 74 65 20 3d 3d 20 31 30 0a 20 20 20 20 20 byte == 10.
5260: 20 72 61 69 73 65 20 52 65 66 75 73 65 64 2e 6e raise Refused.n
5270: 65 77 28 22 6c 69 6e 65 20 74 6f 6f 20 6c 6f 6e ew("line too lon
5280: 67 22 29 20 69 66 20 62 79 74 65 73 2e 73 69 7a g") if bytes.siz
5290: 65 20 3e 3d 20 6c 69 6d 69 74 0a 20 20 20 20 20 e >= limit.
52a0: 20 62 79 74 65 73 2e 77 72 69 74 65 5f 62 79 74 bytes.write_byt
52b0: 65 28 62 79 74 65 29 0a 20 20 20 20 65 6e 64 0a e(byte). end.
52c0: 20 20 20 20 62 79 74 65 73 2e 74 6f 5f 73 0a 20 bytes.to_s.
52d0: 20 72 65 73 63 75 65 20 49 4f 3a 3a 54 69 6d 65 rescue IO::Time
52e0: 6f 75 74 45 72 72 6f 72 0a 20 20 20 20 72 61 69 outError. rai
52f0: 73 65 20 52 65 66 75 73 65 64 2e 6e 65 77 28 22 se Refused.new("
5300: 72 65 71 75 65 73 74 20 6e 6f 74 20 72 65 63 65 request not rece
5310: 69 76 65 64 20 77 69 74 68 69 6e 20 32 20 73 22 ived within 2 s"
5320: 29 0a 20 20 65 6e 64 0a 0a 20 20 64 65 66 20 73 ). end.. def s
5330: 65 6c 66 2e 73 65 72 76 65 28 73 6f 63 6b 65 74 elf.serve(socket
5340: 20 3a 20 55 4e 49 58 53 6f 63 6b 65 74 2c 20 72 : UNIXSocket, r
5350: 65 70 6f 73 69 74 6f 72 69 65 73 20 3a 20 53 74 epositories : St
5360: 72 69 6e 67 2c 20 73 74 61 72 74 65 64 20 3a 20 ring, started :
5370: 54 69 6d 65 2c 20 62 75 73 79 20 3a 20 42 6f 6f Time, busy : Boo
5380: 6c 29 20 3a 20 4e 69 6c 0a 20 20 20 20 72 65 70 l) : Nil. rep
5390: 6f 73 69 74 6f 72 79 20 3d 20 22 2d 22 0a 20 20 ository = "-".
53a0: 20 20 63 6f 75 6e 74 20 3d 20 30 0a 20 20 20 20 count = 0.
53b0: 6f 75 74 63 6f 6d 65 20 3d 20 22 72 65 66 75 73 outcome = "refus
53c0: 65 64 22 0a 20 20 20 20 73 6f 63 6b 65 74 2e 77 ed". socket.w
53d0: 72 69 74 65 5f 74 69 6d 65 6f 75 74 20 3d 20 32 rite_timeout = 2
53e0: 2e 73 65 63 6f 6e 64 73 0a 20 20 20 20 63 72 65 .seconds. cre
53f0: 64 65 6e 74 69 61 6c 73 20 3d 20 52 65 63 65 69 dentials = Recei
5400: 70 74 50 65 65 72 3a 3a 43 72 65 64 65 6e 74 69 ptPeer::Credenti
5410: 61 6c 73 2e 6e 65 77 0a 20 20 20 20 73 69 7a 65 als.new. size
5420: 20 3d 20 73 69 7a 65 6f 66 28 52 65 63 65 69 70 = sizeof(Receip
5430: 74 50 65 65 72 3a 3a 43 72 65 64 65 6e 74 69 61 tPeer::Credentia
5440: 6c 73 29 2e 74 6f 5f 75 33 32 0a 20 20 20 20 69 ls).to_u32. i
5450: 66 20 52 65 63 65 69 70 74 50 65 65 72 2e 67 65 f ReceiptPeer.ge
5460: 74 73 6f 63 6b 6f 70 74 28 73 6f 63 6b 65 74 2e tsockopt(socket.
5470: 66 64 2c 20 31 2c 20 31 37 2c 20 70 6f 69 6e 74 fd, 1, 17, point
5480: 65 72 6f 66 28 63 72 65 64 65 6e 74 69 61 6c 73 erof(credentials
5490: 29 2c 20 70 6f 69 6e 74 65 72 6f 66 28 73 69 7a ), pointerof(siz
54a0: 65 29 29 20 21 3d 20 30 20 7c 7c 20 73 69 7a 65 e)) != 0 || size
54b0: 20 21 3d 20 73 69 7a 65 6f 66 28 52 65 63 65 69 != sizeof(Recei
54c0: 70 74 50 65 65 72 3a 3a 43 72 65 64 65 6e 74 69 ptPeer::Credenti
54d0: 61 6c 73 29 20 7c 7c 20 63 72 65 64 65 6e 74 69 als) || credenti
54e0: 61 6c 73 2e 75 69 64 20 21 3d 20 4c 69 62 43 2e als.uid != LibC.
54f0: 67 65 74 75 69 64 0a 20 20 20 20 20 20 72 61 69 getuid. rai
5500: 73 65 20 52 65 66 75 73 65 64 2e 6e 65 77 28 22 se Refused.new("
5510: 70 65 65 72 20 75 69 64 20 64 69 66 66 65 72 73 peer uid differs
5520: 22 29 0a 20 20 20 20 65 6e 64 0a 20 20 20 20 69 "). end. i
5530: 66 20 62 75 73 79 0a 20 20 20 20 20 20 6f 75 74 f busy. out
5540: 63 6f 6d 65 20 3d 20 22 62 75 73 79 22 0a 20 20 come = "busy".
5550: 20 20 20 20 73 6f 63 6b 65 74 2e 70 75 74 73 28 socket.puts(
5560: 7b 75 6e 61 76 61 69 6c 61 62 6c 65 3a 20 22 62 {unavailable: "b
5570: 75 73 79 22 2c 20 72 65 74 72 79 5f 61 66 74 65 usy", retry_afte
5580: 72 3a 20 31 7d 2e 74 6f 5f 6a 73 6f 6e 29 0a 20 r: 1}.to_json).
5590: 20 20 20 20 20 72 65 74 75 72 6e 0a 20 20 20 20 return.
55a0: 65 6e 64 0a 20 20 20 20 69 64 65 6e 74 69 74 79 end. identity
55b0: 20 3d 20 69 64 65 6e 74 69 74 79 28 6c 69 6e 65 = identity(line
55c0: 28 73 6f 63 6b 65 74 2c 20 31 30 32 34 2c 20 73 (socket, 1024, s
55d0: 74 61 72 74 65 64 20 2b 20 32 2e 73 65 63 6f 6e tarted + 2.secon
55e0: 64 73 29 2c 20 72 65 70 6f 73 69 74 6f 72 69 65 ds), repositorie
55f0: 73 29 0a 20 20 20 20 72 65 70 6f 73 69 74 6f 72 s). repositor
5600: 79 20 3d 20 69 64 65 6e 74 69 74 79 2e 72 65 70 y = identity.rep
5610: 6f 73 69 74 6f 72 79 0a 20 20 20 20 72 65 71 75 ository. requ
5620: 65 73 74 20 3d 20 72 65 71 75 65 73 74 28 6c 69 est = request(li
5630: 6e 65 28 73 6f 63 6b 65 74 2c 20 38 31 39 32 2c ne(socket, 8192,
5640: 20 73 74 61 72 74 65 64 20 2b 20 32 2e 73 65 63 started + 2.sec
5650: 6f 6e 64 73 29 29 0a 20 20 20 20 63 6f 75 6e 74 onds)). count
5660: 20 3d 20 72 65 71 75 65 73 74 2e 61 72 74 69 66 = request.artif
5670: 61 63 74 73 2e 73 69 7a 65 0a 20 20 20 20 69 66 acts.size. if
5680: 20 69 64 65 6e 74 69 74 79 2e 63 61 70 61 62 69 identity.capabi
5690: 6c 69 74 69 65 73 2e 69 6e 63 6c 75 64 65 73 3f lities.includes?
56a0: 28 27 6f 27 29 0a 20 20 20 20 20 20 64 65 61 64 ('o'). dead
56b0: 6c 69 6e 65 20 3d 20 54 69 6d 65 2e 75 74 63 20 line = Time.utc
56c0: 2b 20 32 2e 73 65 63 6f 6e 64 73 0a 20 20 20 20 + 2.seconds.
56d0: 20 20 61 6e 73 77 65 72 20 3d 20 6c 6f 6f 70 20 answer = loop
56e0: 64 6f 0a 20 20 20 20 20 20 20 20 62 65 67 69 6e do. begin
56f0: 0a 20 20 20 20 20 20 20 20 20 20 64 62 20 3d 20 . db =
5700: 44 61 74 61 62 61 73 65 2e 6e 65 77 28 46 69 6c Database.new(Fil
5710: 65 2e 6a 6f 69 6e 28 72 65 70 6f 73 69 74 6f 72 e.join(repositor
5720: 69 65 73 2c 20 72 65 70 6f 73 69 74 6f 72 79 20 ies, repository
5730: 2b 20 22 2e 66 6f 73 73 69 6c 22 29 29 0a 20 20 + ".fossil")).
5740: 20 20 20 20 20 20 20 20 62 65 67 69 6e 0a 20 20 begin.
5750: 20 20 20 20 20 20 20 20 20 20 62 72 65 61 6b 20 break
5760: 64 62 2e 61 6e 73 77 65 72 28 69 64 65 6e 74 69 db.answer(identi
5770: 74 79 2c 20 72 65 71 75 65 73 74 29 0a 20 20 20 ty, request).
5780: 20 20 20 20 20 20 20 65 6e 73 75 72 65 0a 20 20 ensure.
5790: 20 20 20 20 20 20 20 20 20 20 64 62 2e 63 6c 6f db.clo
57a0: 73 65 0a 20 20 20 20 20 20 20 20 20 20 65 6e 64 se. end
57b0: 0a 20 20 20 20 20 20 20 20 72 65 73 63 75 65 20 . rescue
57c0: 65 78 20 3a 20 55 6e 61 76 61 69 6c 61 62 6c 65 ex : Unavailable
57d0: 0a 20 20 20 20 20 20 20 20 20 20 72 65 6d 61 69 . remai
57e0: 6e 69 6e 67 20 3d 20 64 65 61 64 6c 69 6e 65 20 ning = deadline
57f0: 2d 20 54 69 6d 65 2e 75 74 63 0a 20 20 20 20 20 - Time.utc.
5800: 20 20 20 20 20 6c 6f 63 6b 65 64 20 3d 20 7b 22 locked = {"
5810: 53 51 4c 49 54 45 5f 42 55 53 59 22 2c 20 22 53 SQLITE_BUSY", "S
5820: 51 4c 49 54 45 5f 4c 4f 43 4b 45 44 22 7d 2e 61 QLITE_LOCKED"}.a
5830: 6e 79 3f 20 7b 20 7c 6e 61 6d 65 7c 20 65 78 2e ny? { |name| ex.
5840: 6d 65 73 73 61 67 65 2e 74 6f 5f 73 2e 73 74 61 message.to_s.sta
5850: 72 74 73 5f 77 69 74 68 3f 28 6e 61 6d 65 29 20 rts_with?(name)
5860: 7d 0a 20 20 20 20 20 20 20 20 20 20 72 61 69 73 }. rais
5870: 65 20 65 78 20 75 6e 6c 65 73 73 20 6c 6f 63 6b e ex unless lock
5880: 65 64 20 26 26 20 72 65 6d 61 69 6e 69 6e 67 20 ed && remaining
5890: 3e 20 54 69 6d 65 3a 3a 53 70 61 6e 2e 7a 65 72 > Time::Span.zer
58a0: 6f 0a 20 20 20 20 20 20 20 20 20 20 23 20 53 51 o. # SQ
58b0: 4c 69 74 65 27 73 20 43 20 62 75 73 79 20 68 61 Lite's C busy ha
58c0: 6e 64 6c 65 72 20 62 6c 6f 63 6b 73 20 65 76 65 ndler blocks eve
58d0: 72 79 20 66 69 62 65 72 3b 20 77 61 69 74 20 69 ry fiber; wait i
58e0: 6e 20 43 72 79 73 74 61 6c 20 69 6e 73 74 65 61 n Crystal instea
58f0: 64 2e 0a 20 20 20 20 20 20 20 20 20 20 73 6c 65 d.. sle
5900: 65 70 20 4d 61 74 68 2e 6d 69 6e 28 35 30 2e 6d ep Math.min(50.m
5910: 69 6c 6c 69 73 65 63 6f 6e 64 73 2c 20 72 65 6d illiseconds, rem
5920: 61 69 6e 69 6e 67 29 0a 20 20 20 20 20 20 20 20 aining).
5930: 20 20 72 61 69 73 65 20 65 78 20 69 66 20 54 69 raise ex if Ti
5940: 6d 65 2e 75 74 63 20 3e 3d 20 64 65 61 64 6c 69 me.utc >= deadli
5950: 6e 65 0a 20 20 20 20 20 20 20 20 65 6e 64 0a 20 ne. end.
5960: 20 20 20 20 20 65 6e 64 0a 20 20 20 20 65 6c 73 end. els
5970: 65 0a 20 20 20 20 20 20 61 6e 73 77 65 72 20 3d e. answer =
5980: 20 41 6e 73 77 65 72 2e 6e 65 77 28 72 65 71 75 Answer.new(requ
5990: 65 73 74 2e 61 72 74 69 66 61 63 74 73 2e 6d 61 est.artifacts.ma
59a0: 70 20 7b 20 7c 68 61 73 68 7c 20 41 72 74 69 66 p { |hash| Artif
59b0: 61 63 74 2e 6e 65 77 28 68 61 73 68 29 20 7d 2c act.new(hash) },
59c0: 20 72 65 71 75 65 73 74 2e 63 61 70 61 62 69 6c request.capabil
59d0: 69 74 69 65 73 5f 6f 66 2e 74 72 79 20 7b 20 7c ities_of.try { |
59e0: 6e 61 6d 65 7c 20 55 73 65 72 2e 6e 65 77 28 6e name| User.new(n
59f0: 61 6d 65 29 20 7d 29 0a 20 20 20 20 65 6e 64 0a ame) }). end.
5a00: 20 20 20 20 73 6f 63 6b 65 74 2e 70 75 74 73 28 socket.puts(
5a10: 61 6e 73 77 65 72 2e 74 6f 5f 6a 73 6f 6e 29 0a answer.to_json).
5a20: 20 20 20 20 6f 75 74 63 6f 6d 65 20 3d 20 22 6f outcome = "o
5a30: 6b 22 0a 20 20 72 65 73 63 75 65 20 65 78 20 3a k". rescue ex :
5a40: 20 52 65 66 75 73 65 64 20 7c 20 4a 53 4f 4e 3a Refused | JSON:
5a50: 3a 50 61 72 73 65 45 78 63 65 70 74 69 6f 6e 0a :ParseException.
5a60: 20 20 20 20 73 6f 63 6b 65 74 2e 70 75 74 73 28 socket.puts(
5a70: 7b 72 65 66 75 73 65 64 3a 20 65 78 2e 69 73 5f {refused: ex.is_
5a80: 61 3f 28 52 65 66 75 73 65 64 29 20 3f 20 65 78 a?(Refused) ? ex
5a90: 2e 6d 65 73 73 61 67 65 20 3a 20 22 69 6e 76 61 .message : "inva
5aa0: 6c 69 64 20 4a 53 4f 4e 22 7d 2e 74 6f 5f 6a 73 lid JSON"}.to_js
5ab0: 6f 6e 29 0a 20 20 72 65 73 63 75 65 20 65 78 20 on). rescue ex
5ac0: 3a 20 55 6e 61 76 61 69 6c 61 62 6c 65 0a 20 20 : Unavailable.
5ad0: 20 20 6f 75 74 63 6f 6d 65 20 3d 20 22 75 6e 61 outcome = "una
5ae0: 76 61 69 6c 61 62 6c 65 22 0a 20 20 20 20 73 6f vailable". so
5af0: 63 6b 65 74 2e 70 75 74 73 28 7b 75 6e 61 76 61 cket.puts({unava
5b00: 69 6c 61 62 6c 65 3a 20 65 78 2e 6d 65 73 73 61 ilable: ex.messa
5b10: 67 65 2c 20 72 65 74 72 79 5f 61 66 74 65 72 3a ge, retry_after:
5b20: 20 31 7d 2e 74 6f 5f 6a 73 6f 6e 29 0a 20 20 72 1}.to_json). r
5b30: 65 73 63 75 65 20 65 78 20 3a 20 49 4f 3a 3a 45 escue ex : IO::E
5b40: 72 72 6f 72 0a 20 20 20 20 72 61 69 73 65 20 65 rror. raise e
5b50: 78 0a 20 20 72 65 73 63 75 65 20 65 78 0a 20 20 x. rescue ex.
5b60: 20 20 23 20 41 6e 79 74 68 69 6e 67 20 75 6e 66 # Anything unf
5b70: 6f 72 65 73 65 65 6e 20 73 74 69 6c 6c 20 61 6e oreseen still an
5b80: 73 77 65 72 73 2c 20 73 6f 20 61 20 63 61 6c 6c swers, so a call
5b90: 65 72 20 6e 65 76 65 72 20 77 61 69 74 73 20 6f er never waits o
5ba0: 6e 20 61 20 73 69 6c 65 6e 74 20 63 6c 6f 73 65 n a silent close
5bb0: 2e 0a 20 20 20 20 6f 75 74 63 6f 6d 65 20 3d 20 .. outcome =
5bc0: 22 75 6e 61 76 61 69 6c 61 62 6c 65 22 0a 20 20 "unavailable".
5bd0: 20 20 73 6f 63 6b 65 74 2e 70 75 74 73 28 7b 75 socket.puts({u
5be0: 6e 61 76 61 69 6c 61 62 6c 65 3a 20 22 69 6e 74 navailable: "int
5bf0: 65 72 6e 61 6c 20 65 72 72 6f 72 22 2c 20 72 65 ernal error", re
5c00: 74 72 79 5f 61 66 74 65 72 3a 20 31 7d 2e 74 6f try_after: 1}.to
5c10: 5f 6a 73 6f 6e 29 0a 20 20 65 6e 73 75 72 65 0a _json). ensure.
5c20: 20 20 20 20 73 6f 63 6b 65 74 2e 63 6c 6f 73 65 socket.close
5c30: 0a 20 20 20 20 53 54 44 45 52 52 2e 70 75 74 73 . STDERR.puts
5c40: 20 22 23 7b 54 69 6d 65 2e 75 74 63 2e 74 6f 5f "#{Time.utc.to_
5c50: 72 66 63 33 33 33 39 7d 20 23 7b 72 65 70 6f 73 rfc3339} #{repos
5c60: 69 74 6f 72 79 7d 20 23 7b 63 6f 75 6e 74 7d 20 itory} #{count}
5c70: 23 7b 6f 75 74 63 6f 6d 65 7d 20 23 7b 28 54 69 #{outcome} #{(Ti
5c80: 6d 65 2e 75 74 63 20 2d 20 73 74 61 72 74 65 64 me.utc - started
5c90: 29 2e 74 6f 74 61 6c 5f 6d 69 6c 6c 69 73 65 63 ).total_millisec
5ca0: 6f 6e 64 73 2e 74 6f 5f 69 36 34 7d 6d 73 22 0a onds.to_i64}ms".
5cb0: 20 20 65 6e 64 0a 0a 20 20 23 20 50 61 73 73 69 end.. # Passi
5cc0: 6e 67 20 61 72 67 75 6d 65 6e 74 73 20 74 68 72 ng arguments thr
5cd0: 6f 75 67 68 20 73 70 61 77 6e 20 63 6f 70 69 65 ough spawn copie
5ce0: 73 20 65 61 63 68 20 61 63 63 65 70 74 65 64 20 s each accepted
5cf0: 63 6f 6e 6e 65 63 74 69 6f 6e 27 73 20 76 61 6c connection's val
5d00: 75 65 73 2e 0a 20 20 64 65 66 20 73 65 6c 66 2e ues.. def self.
5d10: 68 61 6e 64 6c 65 28 73 6f 63 6b 65 74 20 3a 20 handle(socket :
5d20: 55 4e 49 58 53 6f 63 6b 65 74 2c 20 72 65 70 6f UNIXSocket, repo
5d30: 73 69 74 6f 72 69 65 73 20 3a 20 53 74 72 69 6e sitories : Strin
5d40: 67 2c 20 73 74 61 72 74 65 64 20 3a 20 54 69 6d g, started : Tim
5d50: 65 2c 20 62 75 73 79 20 3a 20 42 6f 6f 6c 2c 20 e, busy : Bool,
5d60: 73 6c 6f 74 73 20 3a 20 43 68 61 6e 6e 65 6c 28 slots : Channel(
5d70: 4e 69 6c 29 29 20 3a 20 4e 69 6c 0a 20 20 20 20 Nil)) : Nil.
5d80: 23 20 42 6f 75 6e 64 20 74 68 65 20 77 68 6f 6c # Bound the whol
5d90: 65 20 63 6f 6e 6e 65 63 74 69 6f 6e 2c 20 69 6e e connection, in
5da0: 63 6c 75 64 69 6e 67 20 61 20 63 61 6c 6c 65 72 cluding a caller
5db0: 20 74 68 61 74 20 73 74 6f 70 73 20 72 65 61 64 that stops read
5dc0: 69 6e 67 20 69 74 73 20 61 6e 73 77 65 72 2e 0a ing its answer..
5dd0: 20 20 20 20 64 6f 6e 65 20 3d 20 43 68 61 6e 6e done = Chann
5de0: 65 6c 28 4e 69 6c 29 2e 6e 65 77 0a 20 20 20 20 el(Nil).new.
5df0: 73 70 61 77 6e 20 64 6f 0a 20 20 20 20 20 20 72 spawn do. r
5e00: 65 6d 61 69 6e 69 6e 67 20 3d 20 73 74 61 72 74 emaining = start
5e10: 65 64 20 2b 20 35 2e 73 65 63 6f 6e 64 73 20 2d ed + 5.seconds -
5e20: 20 54 69 6d 65 2e 75 74 63 0a 20 20 20 20 20 20 Time.utc.
5e30: 69 66 20 72 65 6d 61 69 6e 69 6e 67 20 3e 20 54 if remaining > T
5e40: 69 6d 65 3a 3a 53 70 61 6e 2e 7a 65 72 6f 0a 20 ime::Span.zero.
5e50: 20 20 20 20 20 20 20 73 65 6c 65 63 74 0a 20 20 select.
5e60: 20 20 20 20 20 20 77 68 65 6e 20 64 6f 6e 65 2e when done.
5e70: 72 65 63 65 69 76 65 3f 0a 20 20 20 20 20 20 20 receive?.
5e80: 20 20 20 6e 65 78 74 0a 20 20 20 20 20 20 20 20 next.
5e90: 77 68 65 6e 20 74 69 6d 65 6f 75 74 28 72 65 6d when timeout(rem
5ea0: 61 69 6e 69 6e 67 29 0a 20 20 20 20 20 20 20 20 aining).
5eb0: 65 6e 64 0a 20 20 20 20 20 20 65 6e 64 0a 20 20 end. end.
5ec0: 20 20 20 20 73 6f 63 6b 65 74 2e 63 6c 6f 73 65 socket.close
5ed0: 0a 20 20 20 20 65 6e 64 0a 20 20 20 20 73 65 72 . end. ser
5ee0: 76 65 28 73 6f 63 6b 65 74 2c 20 72 65 70 6f 73 ve(socket, repos
5ef0: 69 74 6f 72 69 65 73 2c 20 73 74 61 72 74 65 64 itories, started
5f00: 2c 20 62 75 73 79 29 0a 20 20 72 65 73 63 75 65 , busy). rescue
5f10: 20 49 4f 3a 3a 45 72 72 6f 72 0a 20 20 20 20 23 IO::Error. #
5f20: 20 54 68 65 20 63 61 6c 6c 65 72 20 6c 65 66 74 The caller left
5f30: 20 62 65 66 6f 72 65 20 69 74 73 20 61 6e 73 77 before its answ
5f40: 65 72 3b 20 73 65 72 76 65 27 73 20 65 6e 73 75 er; serve's ensu
5f50: 72 65 20 6c 6f 67 67 65 64 20 74 68 65 20 63 6f re logged the co
5f60: 6e 6e 65 63 74 69 6f 6e 2e 0a 20 20 65 6e 73 75 nnection.. ensu
5f70: 72 65 0a 20 20 20 20 64 6f 6e 65 2e 74 72 79 28 re. done.try(
5f80: 26 2e 63 6c 6f 73 65 29 0a 20 20 20 20 73 6c 6f &.close). slo
5f90: 74 73 2e 72 65 63 65 69 76 65 20 75 6e 6c 65 73 ts.receive unles
5fa0: 73 20 62 75 73 79 0a 20 20 65 6e 64 0a 0a 20 20 s busy. end..
5fb0: 64 65 66 20 73 65 6c 66 2e 72 75 6e 20 3a 20 4e def self.run : N
5fc0: 69 6c 0a 20 20 20 20 61 62 6f 72 74 20 22 55 73 il. abort "Us
5fd0: 61 67 65 3a 20 72 65 63 65 69 70 74 73 20 53 4f age: receipts SO
5fe0: 43 4b 45 54 20 52 45 50 4f 53 49 54 4f 52 49 45 CKET REPOSITORIE
5ff0: 53 22 2c 20 32 20 75 6e 6c 65 73 73 20 41 52 47 S", 2 unless ARG
6000: 56 2e 73 69 7a 65 20 3d 3d 20 32 0a 20 20 20 20 V.size == 2.
6010: 73 6f 63 6b 65 74 5f 70 61 74 68 2c 20 72 65 70 socket_path, rep
6020: 6f 73 69 74 6f 72 69 65 73 20 3d 20 41 52 47 56 ositories = ARGV
6030: 0a 20 20 20 20 46 69 6c 65 2e 64 65 6c 65 74 65 . File.delete
6040: 28 73 6f 63 6b 65 74 5f 70 61 74 68 29 20 69 66 (socket_path) if
6050: 20 46 69 6c 65 2e 65 78 69 73 74 73 3f 28 73 6f File.exists?(so
6060: 63 6b 65 74 5f 70 61 74 68 29 0a 20 20 20 20 73 cket_path). s
6070: 65 72 76 65 72 20 3d 20 55 4e 49 58 53 65 72 76 erver = UNIXServ
6080: 65 72 2e 6e 65 77 28 73 6f 63 6b 65 74 5f 70 61 er.new(socket_pa
6090: 74 68 29 0a 20 20 20 20 46 69 6c 65 2e 63 68 6d th). File.chm
60a0: 6f 64 28 73 6f 63 6b 65 74 5f 70 61 74 68 2c 20 od(socket_path,
60b0: 30 6f 36 30 30 29 0a 20 20 20 20 73 6c 6f 74 73 0o600). slots
60c0: 20 3d 20 43 68 61 6e 6e 65 6c 28 4e 69 6c 29 2e = Channel(Nil).
60d0: 6e 65 77 28 38 29 0a 20 20 20 20 77 68 69 6c 65 new(8). while
60e0: 20 73 6f 63 6b 65 74 20 3d 20 73 65 72 76 65 72 socket = server
60f0: 2e 61 63 63 65 70 74 3f 0a 20 20 20 20 20 20 73 .accept?. s
6100: 74 61 72 74 65 64 20 3d 20 54 69 6d 65 2e 75 74 tarted = Time.ut
6110: 63 0a 20 20 20 20 20 20 62 75 73 79 20 3d 20 73 c. busy = s
6120: 65 6c 65 63 74 0a 20 20 20 20 20 20 77 68 65 6e elect. when
6130: 20 73 6c 6f 74 73 2e 73 65 6e 64 28 6e 69 6c 29 slots.send(nil)
6140: 20 74 68 65 6e 20 66 61 6c 73 65 0a 20 20 20 20 then false.
6150: 20 20 65 6c 73 65 0a 20 20 20 20 20 20 20 20 74 else. t
6160: 72 75 65 0a 20 20 20 20 20 20 65 6e 64 0a 20 20 rue. end.
6170: 20 20 20 20 69 66 20 62 75 73 79 0a 20 20 20 20 if busy.
6180: 20 20 20 20 68 61 6e 64 6c 65 28 73 6f 63 6b 65 handle(socke
6190: 74 2c 20 72 65 70 6f 73 69 74 6f 72 69 65 73 2c t, repositories,
61a0: 20 73 74 61 72 74 65 64 2c 20 62 75 73 79 2c 20 started, busy,
61b0: 73 6c 6f 74 73 29 0a 20 20 20 20 20 20 65 6c 73 slots). els
61c0: 65 0a 20 20 20 20 20 20 20 20 73 70 61 77 6e 20 e. spawn
61d0: 68 61 6e 64 6c 65 28 73 6f 63 6b 65 74 2c 20 72 handle(socket, r
61e0: 65 70 6f 73 69 74 6f 72 69 65 73 2c 20 73 74 61 epositories, sta
61f0: 72 74 65 64 2c 20 62 75 73 79 2c 20 73 6c 6f 74 rted, busy, slot
6200: 73 29 0a 20 20 20 20 20 20 65 6e 64 0a 20 20 20 s). end.
6210: 20 65 6e 64 0a 20 20 65 6e 64 0a 65 6e 64 0a 0a end. end.end..
6220: 52 65 63 65 69 70 74 73 2e 72 75 6e 0a 00 00 00 Receipts.run....
6230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
62a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
62b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
62c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
62d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
62e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
62f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
63a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
63b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
63c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
63d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
63e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
63f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6400: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69 scripts-for-revi
6410: 65 77 2d 32 2f 72 65 63 6f 72 64 2e 74 78 74 00 ew-2/record.txt.
6420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6460: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30 ....0000644.0000
6470: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 000.0000000.0000
6480: 30 30 31 37 36 35 30 00 31 35 32 37 30 35 32 33 0017650.15270523
6490: 31 32 32 00 30 31 35 35 31 34 00 20 30 00 00 00 122.015514. 0...
64a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
64b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
64c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
64d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
64e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
64f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6500: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00 .ustar .root...
6510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6520: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00 .........root...
6530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6540: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6560: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6570: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
65a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
65b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
65c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
65d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
65e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
65f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
6600: 0a 3d 3d 20 62 75 69 6c 64 3a 20 73 74 61 72 74 .== build: start
6610: 2d 6f 63 78 2c 20 72 65 63 65 69 70 74 73 20 61 -ocx, receipts a
6620: 6e 64 20 6f 63 78 32 20 66 72 6f 6d 20 2f 76 61 nd ocx2 from /va
6630: 72 2f 6c 69 62 2f 6f 63 78 2f 61 67 65 6e 74 73 r/lib/ocx/agents
6640: 2f 6f 70 75 73 2d 6f 63 78 32 2f 70 72 6f 6a 65 /opus-ocx2/proje
6650: 63 74 73 2f 6f 6c 6c 61 6d 61 2f 62 32 64 65 38 cts/ollama/b2de8
6660: 62 30 63 32 63 36 37 30 30 65 62 35 63 36 38 36 b0c2c6700eb5c686
6670: 62 31 37 34 37 62 35 61 61 61 31 65 32 64 32 64 b1747b5aaa1e2d2d
6680: 30 31 33 37 30 61 63 35 35 66 32 65 35 61 31 35 01370ac55f2e5a15
6690: 31 61 36 37 65 30 33 62 34 38 61 2f 74 72 65 65 1a67e03b48a/tree
66a0: 38 0a 0a 3d 3d 20 73 65 72 76 65 64 20 72 65 70 8..== served rep
66b0: 6f 73 69 74 6f 72 79 3a 20 64 65 6d 6f 2e 66 6f ository: demo.fo
66c0: 73 73 69 6c 2c 20 75 73 65 72 73 2c 20 63 68 61 ssil, users, cha
66d0: 74 20 74 69 6d 65 6c 69 6e 65 0a 2d 72 77 2d 2d t timeline.-rw--
66e0: 2d 2d 2d 2d 2d 20 20 20 20 31 20 72 6f 6f 74 20 ----- 1 root
66f0: 20 20 20 20 72 6f 6f 74 20 20 20 20 20 20 20 20 root
6700: 32 31 37 30 38 38 20 4f 63 74 20 32 39 20 30 31 217088 Oct 29 01
6710: 3a 34 38 20 2f 74 6d 70 2f 73 41 2d 33 37 30 37 :48 /tmp/sA-3707
6720: 34 35 32 2f 66 6f 73 73 69 6c 2f 64 65 6d 6f 2e 452/fossil/demo.
6730: 66 6f 73 73 69 6c 0a 0a 3d 3d 20 72 65 63 65 69 fossil..== recei
6740: 70 74 20 73 65 72 76 69 63 65 2c 20 74 68 72 6f pt service, thro
6750: 75 67 68 20 74 68 65 20 6c 61 75 6e 63 68 65 72 ugh the launcher
6760: 27 73 20 72 65 63 65 69 70 74 73 20 70 6f 6c 69 's receipts poli
6770: 63 79 0a 74 6f 74 61 6c 20 30 0a 73 72 77 2d 2d cy.total 0.srw--
6780: 2d 2d 2d 2d 2d 20 20 20 20 31 20 72 6f 6f 74 20 ----- 1 root
6790: 20 20 20 20 72 6f 6f 74 20 20 20 20 20 20 20 20 root
67a0: 20 20 20 20 20 30 20 4f 63 74 20 32 39 20 30 31 0 Oct 29 01
67b0: 3a 34 38 20 72 65 63 65 69 70 74 73 2e 73 6f 63 :48 receipts.soc
67c0: 6b 0a 0a 3d 3d 20 46 6f 73 73 69 6c 20 73 65 72 k..== Fossil ser
67d0: 76 65 72 2c 20 69 74 73 20 65 78 74 72 6f 6f 74 ver, its extroot
67e0: 27 73 20 6f 63 78 32 2d 63 67 69 20 74 68 65 20 's ocx2-cgi the
67f0: 6c 61 75 6e 63 68 65 72 0a 0a 3d 3d 20 74 68 65 launcher..== the
6800: 20 64 61 65 6d 6f 6e 27 73 20 6e 6f 64 65 3a 20 daemon's node:
6810: 6e 6f 64 65 73 2e 6a 73 6f 6e 20 61 6e 64 20 63 nodes.json and c
6820: 72 65 64 65 6e 74 69 61 6c 73 0a 0a 3d 3d 20 63 redentials..== c
6830: 6f 6e 66 69 6e 65 64 20 64 61 65 6d 6f 6e 2c 20 onfined daemon,
6840: 74 68 72 6f 75 67 68 20 74 68 65 20 6c 61 75 6e through the laun
6850: 63 68 65 72 27 73 20 64 61 65 6d 6f 6e 20 70 6f cher's daemon po
6860: 6c 69 63 79 0a 7b 22 77 68 65 6e 22 3a 22 32 30 licy.{"when":"20
6870: 32 36 2d 31 30 2d 32 39 54 30 31 3a 34 39 3a 32 26-10-29T01:49:2
6880: 32 5a 22 2c 22 63 6c 6f 6e 65 22 3a 22 2f 74 6d 2Z","clone":"/tm
6890: 70 2f 73 41 2d 33 37 30 37 34 35 32 2f 64 61 65 p/sA-3707452/dae
68a0: 6d 6f 6e 2f 6e 6f 64 65 2f 63 6c 6f 6e 65 73 2f mon/node/clones/
68b0: 64 65 6d 6f 2e 66 6f 73 73 69 6c 22 2c 22 63 6f demo.fossil","co
68c0: 6d 70 6c 65 74 69 6f 6e 22 3a 31 2c 22 6e 65 77 mpletion":1,"new
68d0: 22 3a 30 2c 22 68 61 6e 64 65 64 22 3a 5b 5d 2c ":0,"handed":[],
68e0: 22 73 65 74 74 6c 65 64 22 3a 30 7d 0a 7b 22 77 "settled":0}.{"w
68f0: 68 65 6e 22 3a 22 32 30 32 36 2d 31 30 2d 32 39 hen":"2026-10-29
6900: 54 30 31 3a 34 39 3a 32 35 5a 22 2c 22 63 6c 6f T01:49:25Z","clo
6910: 6e 65 22 3a 22 2f 74 6d 70 2f 73 41 2d 33 37 30 ne":"/tmp/sA-370
6920: 37 34 35 32 2f 64 61 65 6d 6f 6e 2f 6e 6f 64 65 7452/daemon/node
6930: 2f 63 6c 6f 6e 65 73 2f 64 65 6d 6f 2e 66 6f 73 /clones/demo.fos
6940: 73 69 6c 22 2c 22 63 6f 6d 70 6c 65 74 69 6f 6e sil","completion
6950: 22 3a 31 2c 22 6e 65 77 22 3a 30 2c 22 68 61 6e ":1,"new":0,"han
6960: 64 65 64 22 3a 5b 5d 2c 22 73 65 74 74 6c 65 64 ded":[],"settled
6970: 22 3a 30 7d 0a 7b 22 77 68 65 6e 22 3a 22 32 30 ":0}.{"when":"20
6980: 32 36 2d 31 30 2d 32 39 54 30 31 3a 34 39 3a 32 26-10-29T01:49:2
6990: 38 5a 22 2c 22 63 6c 6f 6e 65 22 3a 22 2f 74 6d 8Z","clone":"/tm
69a0: 70 2f 73 41 2d 33 37 30 37 34 35 32 2f 64 61 65 p/sA-3707452/dae
69b0: 6d 6f 6e 2f 6e 6f 64 65 2f 63 6c 6f 6e 65 73 2f mon/node/clones/
69c0: 64 65 6d 6f 2e 66 6f 73 73 69 6c 22 2c 22 63 6f demo.fossil","co
69d0: 6d 70 6c 65 74 69 6f 6e 22 3a 31 2c 22 6e 65 77 mpletion":1,"new
69e0: 22 3a 30 2c 22 68 61 6e 64 65 64 22 3a 5b 5d 2c ":0,"handed":[],
69f0: 22 73 65 74 74 6c 65 64 22 3a 30 7d 0a 0a 3d 3d "settled":0}..==
6a00: 20 74 77 6f 20 72 75 6e 73 2c 20 70 6f 73 74 65 two runs, poste
6a10: 64 20 62 79 20 62 6f 73 73 20 28 73 65 74 75 70 d by boss (setup
6a20: 29 20 74 68 72 6f 75 67 68 20 46 6f 73 73 69 6c ) through Fossil
6a30: 27 73 20 66 6f 72 6d 0a 73 61 6e 64 62 6f 78 65 's form.sandboxe
6a40: 64 20 72 75 6e 20 64 30 39 38 39 65 63 64 62 32 d run d0989ecdb2
6a50: 0a 75 6e 63 6f 6e 66 69 6e 65 64 20 72 75 6e 20 .unconfined run
6a60: 33 64 65 64 62 31 30 64 64 34 0a 0a 3d 3d 20 77 3dedb10dd4..== w
6a70: 68 61 74 20 74 68 65 20 72 75 6e 73 20 70 6f 73 hat the runs pos
6a80: 74 65 64 20 28 73 74 61 72 74 65 64 2c 20 6b 65 ted (started, ke
6a90: 70 74 20 6c 6f 67 73 2c 20 65 6e 64 65 64 29 0a pt logs, ended).
6aa0: 27 64 30 39 38 39 65 63 64 62 32 27 2c 27 62 6f 'd0989ecdb2','bo
6ab0: 73 73 27 2c 27 44 20 32 30 32 36 2d 31 30 2d 32 ss','D 2026-10-2
6ac0: 39 54 30 31 3a 34 39 3a 33 31 2e 30 34 39 20 48 9T01:49:31.049 H
6ad0: 20 73 61 6e 64 62 6f 78 65 64 5c 73 70 72 6f 62 sandboxed\sprob
6ae0: 65 20 4e 20 74 65 78 74 2f 78 2d 6d 61 72 6b 64 e N text/x-markd
6af0: 6f 77 6e 20 55 20 62 6f 73 73 20 57 20 31 30 30 own U boss W 100
6b00: 37 20 60 60 60 6a 73 6f 6e 20 7b 22 6f 63 78 22 7 ```json {"ocx"
6b10: 3a 20 31 2c 20 22 6b 69 6e 64 22 3a 20 22 72 75 : 1, "kind": "ru
6b20: 6e 22 2c 20 22 6e 6f 64 65 22 3a 20 22 64 65 6d n", "node": "dem
6b30: 6f 2d 6e 6f 64 65 22 2c 20 22 6f 62 6a 65 63 74 o-node", "object
6b40: 69 76 65 22 3a 20 22 50 72 6f 62 65 20 66 72 6f ive": "Probe fro
6b50: 6d 20 61 6e 20 61 67 65 6e 74 20 73 61 6e 64 62 m an agent sandb
6b60: 6f 78 2e 22 2c 20 22 77 6f 72 6b 73 70 61 63 65 ox.", "workspace
6b70: 22 3a 20 5b 7b 22 73 74 65 70 22 3a 20 22 64 69 ": [{"step": "di
6b80: 72 65 63 74 6f 72 79 22 2c 20 22 61 74 22 3a 20 rectory", "at":
6b90: 22 24 24 72 6f 6f 74 24 24 2f 24 24 75 73 65 72 "$$root$$/$$user
6ba0: 24 24 2f 70 72 6f 6a 65 63 74 73 2f 24 24 70 72 $$/projects/$$pr
6bb0: 6f 6a 65 63 74 24 24 2f 24 24 72 75 6e 24 24 22 oject$$/$$run$$"
6bc0: 7d 5d 2c 20 22 73 61 6e 64 62 6f 78 22 3a 20 7b }], "sandbox": {
6bd0: 22 75 69 64 22 3a 20 31 30 30 30 2c 20 22 67 69 "uid": 1000, "gi
6be0: 64 22 3a 20 31 30 30 30 2c 20 22 63 61 70 61 62 d": 1000, "capab
6bf0: 69 6c 69 74 69 65 73 22 3a 20 5b 5d 2c 20 22 6e ilities": [], "n
6c00: 65 74 77 6f 72 6b 22 3a 20 66 61 6c 73 65 2c 20 etwork": false,
6c10: 22 6f 77 6e 5f 73 65 73 73 69 6f 6e 22 3a 20 74 "own_session": t
6c20: 72 75 65 2c 20 22 65 6e 64 73 5f 77 69 74 68 5f rue, "ends_with_
6c30: 6e 6f 64 65 22 3a 20 74 72 75 65 2c 20 22 72 65 node": true, "re
6c40: 61 64 5f 6f 6e 6c 79 22 3a 20 5b 22 2f 75 73 72 ad_only": ["/usr
6c50: 22 2c 20 22 2f 62 69 6e 22 2c 20 22 2f 6c 69 62 ", "/bin", "/lib
6c60: 22 2c 20 22 2f 6c 69 62 36 34 22 5d 2c 20 22 72 ", "/lib64"], "r
6c70: 65 61 64 5f 77 72 69 74 65 22 3a 20 5b 22 24 24 ead_write": ["$$
6c80: 77 6f 72 6b 73 70 61 63 65 24 24 22 5d 2c 20 22 workspace$$"], "
6c90: 68 6f 6d 65 73 22 3a 20 5b 5d 2c 20 22 66 69 6c homes": [], "fil
6ca0: 65 73 22 3a 20 5b 5d 2c 20 22 70 72 6f 63 22 3a es": [], "proc":
6cb0: 20 22 2f 70 72 6f 63 22 2c 20 22 64 65 76 22 3a "/proc", "dev":
6cc0: 20 22 2f 64 65 76 22 2c 20 22 74 6d 70 22 3a 20 "/dev", "tmp":
6cd0: 22 2f 74 6d 70 22 7d 2c 20 22 6c 69 6d 69 74 73 "/tmp"}, "limits
6ce0: 22 3a 20 7b 22 77 61 6c 6c 22 3a 20 22 32 6d 22 ": {"wall": "2m"
6cf0: 7d 2c 20 22 70 72 6f 63 65 73 73 22 3a 20 7b 22 }, "process": {"
6d00: 75 73 65 72 22 3a 20 22 62 75 69 6c 64 27 0a 27 user": "build'.'
6d10: 33 64 65 64 62 31 30 64 64 34 27 2c 27 62 6f 73 3dedb10dd4','bos
6d20: 73 27 2c 27 44 20 32 30 32 36 2d 31 30 2d 32 39 s','D 2026-10-29
6d30: 54 30 31 3a 34 39 3a 33 31 2e 31 35 35 20 48 20 T01:49:31.155 H
6d40: 75 6e 63 6f 6e 66 69 6e 65 64 5c 73 70 72 6f 62 unconfined\sprob
6d50: 65 20 4e 20 74 65 78 74 2f 78 2d 6d 61 72 6b 64 e N text/x-markd
6d60: 6f 77 6e 20 55 20 62 6f 73 73 20 57 20 36 34 32 own U boss W 642
6d70: 20 60 60 60 6a 73 6f 6e 20 7b 22 6f 63 78 22 3a ```json {"ocx":
6d80: 20 31 2c 20 22 6b 69 6e 64 22 3a 20 22 72 75 6e 1, "kind": "run
6d90: 22 2c 20 22 6e 6f 64 65 22 3a 20 22 64 65 6d 6f ", "node": "demo
6da0: 2d 6e 6f 64 65 22 2c 20 22 6f 62 6a 65 63 74 69 -node", "objecti
6db0: 76 65 22 3a 20 22 50 72 6f 62 65 20 66 72 6f 6d ve": "Probe from
6dc0: 20 74 68 65 20 64 61 65 6d 6f 6e 20 69 74 73 65 the daemon itse
6dd0: 6c 66 2c 20 75 6e 63 6f 6e 66 69 6e 65 64 20 62 lf, unconfined b
6de0: 79 20 61 6e 79 20 72 75 6e 20 73 61 6e 64 62 6f y any run sandbo
6df0: 78 2e 22 2c 20 22 70 72 6f 63 65 73 73 22 3a 20 x.", "process":
6e00: 7b 22 75 73 65 72 22 3a 20 22 62 75 69 6c 64 65 {"user": "builde
6e10: 72 22 2c 20 22 63 6f 6d 6d 61 6e 64 22 3a 20 5b r", "command": [
6e20: 22 2f 62 69 6e 2f 73 68 22 2c 20 22 2d 63 22 2c "/bin/sh", "-c",
6e30: 20 22 65 63 68 6f 20 75 69 64 3d 24 28 69 64 20 "echo uid=$(id
6e40: 2d 75 29 3b 20 66 6f 72 20 70 20 69 6e 20 2f 74 -u); for p in /t
6e50: 6d 70 2f 73 41 2d 33 37 30 37 34 35 32 2f 66 6f mp/sA-3707452/fo
6e60: 73 73 69 6c 20 2f 74 6d 70 2f 73 41 2d 33 37 30 ssil /tmp/sA-370
6e70: 37 34 35 32 2f 66 6f 73 73 69 6c 2f 64 65 6d 6f 7452/fossil/demo
6e80: 2e 66 6f 73 73 69 6c 3b 20 64 6f 20 5b 20 2d 65 .fossil; do [ -e
6e90: 20 24 70 20 5d 20 26 26 20 65 63 68 6f 20 56 49 $p ] && echo VI
6ea0: 53 49 42 4c 45 20 24 70 20 7c 7c 20 65 63 68 6f SIBLE $p || echo
6eb0: 20 61 62 73 65 6e 74 20 24 70 3b 20 64 6f 6e 65 absent $p; done
6ec0: 3b 20 68 65 61 64 20 2d 63 20 31 36 20 2f 74 6d ; head -c 16 /tm
6ed0: 70 2f 73 41 2d 33 37 30 37 34 35 32 2f 66 6f 73 p/sA-3707452/fos
6ee0: 73 69 6c 2f 64 65 6d 6f 2e 66 6f 73 73 69 6c 20 sil/demo.fossil
6ef0: 3e 2f 64 65 76 2f 6e 75 6c 6c 20 32 3e 26 31 20 >/dev/null 2>&1
6f00: 26 26 20 65 63 68 6f 20 52 45 41 44 2d 4f 4b 20 && echo READ-OK
6f10: 7c 7c 20 65 63 68 6f 20 72 65 61 64 2d 72 65 66 || echo read-ref
6f20: 75 73 65 64 3b 20 6e 3d 30 3b 20 66 6f 72 20 78 used; n=0; for x
6f30: 20 69 6e 20 2f 70 72 6f 63 2f 5b 30 2d 39 5d 2a in /proc/[0-9]*
6f40: 2f 72 6f 6f 74 2f 74 6d 70 2f 73 41 2d 33 37 30 /root/tmp/sA-370
6f50: 37 34 35 32 2f 66 6f 73 73 69 6c 2f 64 65 6d 6f 7452/fossil/demo
6f60: 2e 66 6f 73 73 69 6c 3b 20 64 6f 20 5b 20 2d 72 .fossil; do [ -r
6f70: 20 24 78 20 5d 20 26 26 20 6e 3d 24 27 0a 27 63 $x ] && n=$'.'c
6f80: 34 37 34 62 64 37 36 37 64 27 2c 27 64 65 6d 6f 474bd767d','demo
6f90: 2d 6e 6f 64 65 27 2c 27 44 20 32 30 32 36 2d 31 -node','D 2026-1
6fa0: 30 2d 32 39 54 30 31 3a 34 39 3a 33 32 2e 35 38 0-29T01:49:32.58
6fb0: 38 20 47 20 64 30 39 38 39 65 63 64 62 32 65 36 8 G d0989ecdb2e6
6fc0: 32 63 63 61 62 66 34 65 38 64 33 62 62 62 61 62 2ccabf4e8d3bbbab
6fd0: 32 65 31 37 37 31 64 34 34 66 32 64 63 36 39 32 2e1771d44f2dc692
6fe0: 38 31 31 62 36 64 66 30 34 62 66 30 31 33 32 35 811b6df04bf01325
6ff0: 61 32 38 64 20 49 20 64 30 39 38 39 65 63 64 62 a28d I d0989ecdb
7000: 32 65 36 32 63 63 61 62 66 34 65 38 64 33 62 62 2e62ccabf4e8d3bb
7010: 62 61 62 32 65 31 37 37 31 64 34 34 66 32 64 63 bab2e1771d44f2dc
7020: 36 39 32 38 31 31 62 36 64 66 30 34 62 66 30 31 692811b6df04bf01
7030: 33 32 35 61 32 38 64 20 4e 20 74 65 78 74 2f 78 325a28d N text/x
7040: 2d 6d 61 72 6b 64 6f 77 6e 20 55 20 64 65 6d 6f -markdown U demo
7050: 2d 6e 6f 64 65 20 57 20 36 38 37 20 60 60 60 6a -node W 687 ```j
7060: 73 6f 6e 20 7b 20 20 22 6f 63 78 22 3a 20 31 2c son { "ocx": 1,
7070: 20 20 22 6b 69 6e 64 22 3a 20 22 73 74 61 72 74 "kind": "start
7080: 65 64 22 2c 20 20 22 6e 6f 64 65 22 3a 20 22 64 ed", "node": "d
7090: 65 6d 6f 2d 6e 6f 64 65 22 2c 20 20 22 63 6c 6f emo-node", "clo
70a0: 6e 65 22 3a 20 22 30 65 34 35 65 63 36 65 30 66 ne": "0e45ec6e0f
70b0: 35 35 37 30 30 36 66 63 63 62 36 38 37 34 63 31 557006fccb6874c1
70c0: 35 62 39 61 33 37 36 62 38 63 39 62 66 33 22 2c 5b9a376b8c9bf3",
70d0: 20 20 22 72 75 6e 22 3a 20 22 64 30 39 38 39 65 "run": "d0989e
70e0: 63 64 62 32 65 36 32 63 63 61 62 66 34 65 38 64 cdb2e62ccabf4e8d
70f0: 33 62 62 62 61 62 32 65 31 37 37 31 64 34 34 66 3bbbab2e1771d44f
7100: 32 64 63 36 39 32 38 31 31 62 36 64 66 30 34 62 2dc692811b6df04b
7110: 66 30 31 33 32 35 61 32 38 64 22 2c 20 20 22 76 f01325a28d", "v
7120: 61 6c 75 65 73 22 3a 20 7b 20 20 20 22 72 6f 6f alues": { "roo
7130: 74 22 3a 20 22 2f 74 6d 70 2f 73 41 2d 33 37 30 t": "/tmp/sA-370
7140: 37 34 35 32 2f 64 61 65 6d 6f 6e 2f 61 67 65 6e 7452/daemon/agen
7150: 74 73 22 2c 20 20 20 22 70 72 6f 6a 65 63 74 22 ts", "project"
7160: 3a 20 22 64 65 6d 6f 22 2c 20 20 20 22 75 73 65 : "demo", "use
7170: 72 22 3a 20 22 62 75 69 6c 64 65 72 22 2c 20 20 r": "builder",
7180: 20 22 72 75 6e 22 3a 20 22 64 30 39 38 39 65 63 "run": "d0989ec
7190: 64 62 32 65 36 32 63 63 61 62 66 34 65 38 64 33 db2e62ccabf4e8d3
71a0: 62 62 62 61 62 32 65 31 37 37 31 64 34 34 66 32 bbbab2e1771d44f2
71b0: 64 63 36 39 32 38 31 31 62 36 64 66 30 34 62 66 dc692811b6df04bf
71c0: 30 31 33 32 35 61 32 38 64 22 2c 20 20 20 22 6e 01325a28d", "n
71d0: 6f 64 65 22 3a 20 22 64 65 6d 6f 2d 6e 6f 64 65 ode": "demo-node
71e0: 22 2c 20 20 20 22 73 65 72 76 65 72 22 3a 20 22 ", "server": "
71f0: 27 0a 27 35 39 66 30 61 62 37 37 33 30 27 2c 27 '.'59f0ab7730','
7200: 64 65 6d 6f 2d 6e 6f 64 65 27 2c 27 44 20 32 30 demo-node','D 20
7210: 32 36 2d 31 30 2d 32 39 54 30 31 3a 34 39 3a 33 26-10-29T01:49:3
7220: 32 2e 36 38 30 20 47 20 64 30 39 38 39 65 63 64 2.680 G d0989ecd
7230: 62 32 65 36 32 63 63 61 62 66 34 65 38 64 33 62 b2e62ccabf4e8d3b
7240: 62 62 61 62 32 65 31 37 37 31 64 34 34 66 32 64 bbab2e1771d44f2d
7250: 63 36 39 32 38 31 31 62 36 64 66 30 34 62 66 30 c692811b6df04bf0
7260: 31 33 32 35 61 32 38 64 20 49 20 64 30 39 38 39 1325a28d I d0989
7270: 65 63 64 62 32 65 36 32 63 63 61 62 66 34 65 38 ecdb2e62ccabf4e8
7280: 64 33 62 62 62 61 62 32 65 31 37 37 31 64 34 34 d3bbbab2e1771d44
7290: 66 32 64 63 36 39 32 38 31 31 62 36 64 66 30 34 f2dc692811b6df04
72a0: 62 66 30 31 33 32 35 61 32 38 64 20 4e 20 74 65 bf01325a28d N te
72b0: 78 74 2f 78 2d 6d 61 72 6b 64 6f 77 6e 20 55 20 xt/x-markdown U
72c0: 64 65 6d 6f 2d 6e 6f 64 65 20 57 20 32 33 32 20 demo-node W 232
72d0: 54 68 65 20 70 72 6f 63 65 73 73 20 66 6f 72 20 The process for
72e0: 74 68 65 20 72 75 6e 20 64 30 39 38 39 65 63 64 the run d0989ecd
72f0: 62 32 65 36 32 63 63 61 62 66 34 65 38 64 33 62 b2e62ccabf4e8d3b
7300: 62 62 61 62 32 65 31 37 37 31 64 34 34 66 32 64 bbab2e1771d44f2d
7310: 63 36 39 32 38 31 31 62 36 64 66 30 34 62 66 30 c692811b6df04bf0
7320: 31 33 32 35 61 32 38 64 20 65 78 69 74 65 64 20 1325a28d exited
7330: 30 2e 20 7e 7e 7e 7e 20 73 74 64 6f 75 74 20 75 0. ~~~~ stdout u
7340: 69 64 3d 31 30 30 30 20 61 62 73 65 6e 74 20 2f id=1000 absent /
7350: 74 6d 70 2f 73 41 2d 33 37 30 37 34 35 32 2f 66 tmp/sA-3707452/f
7360: 6f 73 73 69 6c 20 61 62 73 65 6e 74 20 2f 74 6d ossil absent /tm
7370: 70 2f 73 41 2d 33 37 30 37 34 35 32 2f 66 6f 73 p/sA-3707452/fos
7380: 73 69 6c 2f 64 65 6d 6f 2e 66 6f 73 73 69 6c 20 sil/demo.fossil
7390: 72 65 61 64 2d 72 65 66 75 73 65 64 20 70 72 6f read-refused pro
73a0: 63 2d 70 61 74 68 73 2d 72 65 61 64 61 62 6c 65 c-paths-readable
73b0: 3d 30 20 7e 7e 7e 7e 20 5a 20 35 61 66 61 38 34 =0 ~~~~ Z 5afa84
73c0: 37 64 64 36 62 66 61 62 30 63 31 62 39 33 64 39 7dd6bfab0c1b93d9
73d0: 61 66 38 61 36 64 36 33 37 65 20 27 0a 27 34 32 af8a6d637e '.'42
73e0: 33 37 33 62 31 61 31 30 27 2c 27 64 65 6d 6f 2d 373b1a10','demo-
73f0: 6e 6f 64 65 27 2c 27 44 20 32 30 32 36 2d 31 30 node','D 2026-10
7400: 2d 32 39 54 30 31 3a 34 39 3a 33 32 2e 36 39 31 -29T01:49:32.691
7410: 20 47 20 64 30 39 38 39 65 63 64 62 32 65 36 32 G d0989ecdb2e62
7420: 63 63 61 62 66 34 65 38 64 33 62 62 62 61 62 32 ccabf4e8d3bbbab2
7430: 65 31 37 37 31 64 34 34 66 32 64 63 36 39 32 38 e1771d44f2dc6928
7440: 31 31 62 36 64 66 30 34 62 66 30 31 33 32 35 61 11b6df04bf01325a
7450: 32 38 64 20 49 20 64 30 39 38 39 65 63 64 62 32 28d I d0989ecdb2
7460: 65 36 32 63 63 61 62 66 34 65 38 64 33 62 62 62 e62ccabf4e8d3bbb
7470: 61 62 32 65 31 37 37 31 64 34 34 66 32 64 63 36 ab2e1771d44f2dc6
7480: 39 32 38 31 31 62 36 64 66 30 34 62 66 30 31 33 92811b6df04bf013
7490: 32 35 61 32 38 64 20 4e 20 74 65 78 74 2f 78 2d 25a28d N text/x-
74a0: 6d 61 72 6b 64 6f 77 6e 20 55 20 64 65 6d 6f 2d markdown U demo-
74b0: 6e 6f 64 65 20 57 20 32 33 36 20 60 60 60 6a 73 node W 236 ```js
74c0: 6f 6e 20 7b 20 20 22 6f 63 78 22 3a 20 31 2c 20 on { "ocx": 1,
74d0: 20 22 6b 69 6e 64 22 3a 20 22 65 6e 64 65 64 22 "kind": "ended"
74e0: 2c 20 20 22 6e 6f 64 65 22 3a 20 22 64 65 6d 6f , "node": "demo
74f0: 2d 6e 6f 64 65 22 2c 20 20 22 63 6c 6f 6e 65 22 -node", "clone"
7500: 3a 20 22 30 65 34 35 65 63 36 65 30 66 35 35 37 : "0e45ec6e0f557
7510: 30 30 36 66 63 63 62 36 38 37 34 63 31 35 62 39 006fccb6874c15b9
7520: 61 33 37 36 62 38 63 39 62 66 33 22 2c 20 20 22 a376b8c9bf3", "
7530: 72 75 6e 22 3a 20 22 64 30 39 38 39 65 63 64 62 run": "d0989ecdb
7540: 32 65 36 32 63 63 61 62 66 34 65 38 64 33 62 62 2e62ccabf4e8d3bb
7550: 62 61 62 32 65 31 37 37 31 64 34 34 66 32 64 63 bab2e1771d44f2dc
7560: 36 39 32 38 31 31 62 36 64 66 30 34 62 66 30 31 692811b6df04bf01
7570: 33 32 35 61 32 38 64 22 2c 20 20 22 6f 75 74 63 325a28d", "outc
7580: 6f 6d 65 22 3a 20 22 65 78 69 74 65 64 22 2c 20 ome": "exited",
7590: 20 22 65 78 69 74 22 3a 20 30 20 7d 20 60 60 60 "exit": 0 } ```
75a0: 20 5a 20 36 38 35 34 30 61 36 62 38 64 66 39 61 Z 68540a6b8df9a
75b0: 38 64 31 35 66 38 37 62 61 31 33 34 62 36 62 61 8d15f87ba134b6ba
75c0: 39 32 35 20 27 0a 27 61 61 62 39 30 37 33 33 61 925 '.'aab90733a
75d0: 37 27 2c 27 64 65 6d 6f 2d 6e 6f 64 65 27 2c 27 7','demo-node','
75e0: 44 20 32 30 32 36 2d 31 30 2d 32 39 54 30 31 3a D 2026-10-29T01:
75f0: 34 39 3a 33 32 2e 38 36 33 20 47 20 33 64 65 64 49:32.863 G 3ded
7600: 62 31 30 64 64 34 31 36 38 32 33 61 33 65 62 64 b10dd416823a3ebd
7610: 32 61 35 37 63 31 61 32 35 66 66 37 31 32 32 61 2a57c1a25ff7122a
7620: 61 39 37 39 39 30 37 37 66 64 34 61 37 64 37 61 a9799077fd4a7d7a
7630: 35 38 38 39 34 31 63 34 34 65 35 39 20 49 20 33 588941c44e59 I 3
7640: 64 65 64 62 31 30 64 64 34 31 36 38 32 33 61 33 dedb10dd416823a3
7650: 65 62 64 32 61 35 37 63 31 61 32 35 66 66 37 31 ebd2a57c1a25ff71
7660: 32 32 61 61 39 37 39 39 30 37 37 66 64 34 61 37 22aa9799077fd4a7
7670: 64 37 61 35 38 38 39 34 31 63 34 34 65 35 39 20 d7a588941c44e59
7680: 4e 20 74 65 78 74 2f 78 2d 6d 61 72 6b 64 6f 77 N text/x-markdow
7690: 6e 20 55 20 64 65 6d 6f 2d 6e 6f 64 65 20 57 20 n U demo-node W
76a0: 32 37 37 20 60 60 60 6a 73 6f 6e 20 7b 20 20 22 277 ```json { "
76b0: 6f 63 78 22 3a 20 31 2c 20 20 22 6b 69 6e 64 22 ocx": 1, "kind"
76c0: 3a 20 22 65 6e 64 65 64 22 2c 20 20 22 6e 6f 64 : "ended", "nod
76d0: 65 22 3a 20 22 64 65 6d 6f 2d 6e 6f 64 65 22 2c e": "demo-node",
76e0: 20 20 22 63 6c 6f 6e 65 22 3a 20 22 30 65 34 35 "clone": "0e45
76f0: 65 63 36 65 30 66 35 35 37 30 30 36 66 63 63 62 ec6e0f557006fccb
7700: 36 38 37 34 63 31 35 62 39 61 33 37 36 62 38 63 6874c15b9a376b8c
7710: 39 62 66 33 22 2c 20 20 22 72 75 6e 22 3a 20 22 9bf3", "run": "
7720: 33 64 65 64 62 31 30 64 64 34 31 36 38 32 33 61 3dedb10dd416823a
7730: 33 65 62 64 32 61 35 37 63 31 61 32 35 66 66 37 3ebd2a57c1a25ff7
7740: 31 32 32 61 61 39 37 39 39 30 37 37 66 64 34 61 122aa9799077fd4a
7750: 37 64 37 61 35 38 38 39 34 31 63 34 34 65 35 39 7d7a588941c44e59
7760: 22 2c 20 20 22 6f 75 74 63 6f 6d 65 22 3a 20 22 ", "outcome": "
7770: 72 65 66 75 73 65 64 22 2c 20 20 22 6e 6f 74 65 refused", "note
7780: 22 3a 20 22 74 68 69 73 20 6e 6f 64 65 20 72 75 ": "this node ru
7790: 6e 73 20 6f 6e 6c 79 20 73 61 6e 64 62 6f 78 65 ns only sandboxe
77a0: 64 20 70 72 6f 63 65 73 73 65 73 22 20 7d 20 60 d processes" } `
77b0: 60 60 20 5a 20 63 62 31 34 30 31 65 65 36 37 34 `` Z cb1401ee674
77c0: 63 38 61 63 33 36 30 62 66 63 34 37 38 39 30 33 c8ac360bfc478903
77d0: 33 33 64 38 30 20 27 0a 0a 3d 3d 20 72 65 63 65 33d80 '..== rece
77e0: 69 70 74 73 20 74 68 72 6f 75 67 68 20 74 68 65 ipts through the
77f0: 20 6c 61 75 6e 63 68 65 72 27 73 20 43 47 49 20 launcher's CGI
7800: 72 6f 75 74 65 2c 20 61 67 61 69 6e 73 74 20 46 route, against F
7810: 6f 73 73 69 6c 27 73 20 6f 77 6e 20 72 65 63 6f ossil's own reco
7820: 72 64 0a 62 6f 73 73 20 7b 22 61 72 74 69 66 61 rd.boss {"artifa
7830: 63 74 73 22 3a 20 5b 7b 22 61 72 74 69 66 61 63 cts": [{"artifac
7840: 74 22 3a 20 22 64 30 39 38 39 65 63 64 62 32 65 t": "d0989ecdb2e
7850: 36 32 63 63 61 62 66 34 65 38 64 33 62 62 62 61 62ccabf4e8d3bbba
7860: 62 32 65 31 37 37 31 64 34 34 66 32 64 63 36 39 b2e1771d44f2dc69
7870: 32 38 31 31 62 36 64 66 30 34 62 66 30 31 33 32 2811b6df04bf0132
7880: 35 61 32 38 64 22 2c 20 22 72 65 63 65 69 76 65 5a28d", "receive
7890: 64 22 3a 20 74 72 75 65 2c 20 22 73 65 6e 64 65 d": true, "sende
78a0: 72 22 3a 20 22 62 6f 73 73 22 2c 20 22 63 61 70 r": "boss", "cap
78b0: 61 62 69 6c 69 74 69 65 73 22 3a 20 22 62 63 64 abilities": "bcd
78c0: 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 efghijklmnopqrst
78d0: 77 7a 32 33 34 35 36 37 41 44 22 2c 20 22 72 65 wz234567AD", "re
78e0: 63 65 69 70 74 22 3a 20 32 2c 20 22 72 69 64 22 ceipt": 2, "rid"
78f0: 3a 20 32 7d 5d 2c 20 22 75 73 65 72 22 3a 20 7b : 2}], "user": {
7900: 22 75 73 65 72 22 3a 20 22 64 65 6d 6f 2d 6e 6f "user": "demo-no
7910: 64 65 22 2c 20 22 63 61 70 61 62 69 6c 69 74 69 de", "capabiliti
7920: 65 73 22 3a 20 22 67 68 6a 6f 72 77 7a 34 43 22 es": "ghjorwz4C"
7930: 2c 20 22 77 69 6b 69 5f 6d 6f 64 65 72 61 74 65 , "wiki_moderate
7940: 64 22 3a 20 66 61 6c 73 65 2c 20 22 74 69 63 6b d": false, "tick
7950: 65 74 5f 6d 6f 64 65 72 61 74 65 64 22 3a 20 66 et_moderated": f
7960: 61 6c 73 65 7d 7d 0a 76 69 65 77 65 72 20 7b 22 alse}}.viewer {"
7970: 61 72 74 69 66 61 63 74 73 22 3a 20 5b 7b 22 61 artifacts": [{"a
7980: 72 74 69 66 61 63 74 22 3a 20 22 64 30 39 38 39 rtifact": "d0989
7990: 65 63 64 62 32 65 36 32 63 63 61 62 66 34 65 38 ecdb2e62ccabf4e8
79a0: 64 33 62 62 62 61 62 32 65 31 37 37 31 64 34 34 d3bbbab2e1771d44
79b0: 66 32 64 63 36 39 32 38 31 31 62 36 64 66 30 34 f2dc692811b6df04
79c0: 62 66 30 31 33 32 35 61 32 38 64 22 2c 20 22 72 bf01325a28d", "r
79d0: 65 63 65 69 76 65 64 22 3a 20 74 72 75 65 2c 20 eceived": true,
79e0: 22 73 65 6e 64 65 72 22 3a 20 22 62 6f 73 73 22 "sender": "boss"
79f0: 2c 20 22 63 61 70 61 62 69 6c 69 74 69 65 73 22 , "capabilities"
7a00: 3a 20 22 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e : "bcdefghijklmn
7a10: 6f 70 71 72 73 74 77 7a 32 33 34 35 36 37 41 44 opqrstwz234567AD
7a20: 22 2c 20 22 72 65 63 65 69 70 74 22 3a 20 32 2c ", "receipt": 2,
7a30: 20 22 72 69 64 22 3a 20 32 7d 5d 2c 20 22 75 73 "rid": 2}], "us
7a40: 65 72 22 3a 20 7b 22 75 73 65 72 22 3a 20 22 64 er": {"user": "d
7a50: 65 6d 6f 2d 6e 6f 64 65 22 2c 20 22 63 61 70 61 emo-node", "capa
7a60: 62 69 6c 69 74 69 65 73 22 3a 20 22 67 68 6a 6f bilities": "ghjo
7a70: 72 77 7a 34 43 22 2c 20 22 77 69 6b 69 5f 6d 6f rwz4C", "wiki_mo
7a80: 64 65 72 61 74 65 64 22 3a 20 66 61 6c 73 65 2c derated": false,
7a90: 20 22 74 69 63 6b 65 74 5f 6d 6f 64 65 72 61 74 "ticket_moderat
7aa0: 65 64 22 3a 20 66 61 6c 73 65 7d 7d 0a 61 6e 6f ed": false}}.ano
7ab0: 6e 20 7b 22 61 72 74 69 66 61 63 74 73 22 3a 20 n {"artifacts":
7ac0: 5b 7b 22 61 72 74 69 66 61 63 74 22 3a 20 22 64 [{"artifact": "d
7ad0: 30 39 38 39 65 63 64 62 32 65 36 32 63 63 61 62 0989ecdb2e62ccab
7ae0: 66 34 65 38 64 33 62 62 62 61 62 32 65 31 37 37 f4e8d3bbbab2e177
7af0: 31 64 34 34 66 32 64 63 36 39 32 38 31 31 62 36 1d44f2dc692811b6
7b00: 64 66 30 34 62 66 30 31 33 32 35 61 32 38 64 22 df04bf01325a28d"
7b10: 2c 20 22 72 65 63 65 69 76 65 64 22 3a 20 74 72 , "received": tr
7b20: 75 65 2c 20 22 73 65 6e 64 65 72 22 3a 20 22 62 ue, "sender": "b
7b30: 6f 73 73 22 2c 20 22 63 61 70 61 62 69 6c 69 74 oss", "capabilit
7b40: 69 65 73 22 3a 20 22 62 63 64 65 66 67 68 69 6a ies": "bcdefghij
7b50: 6b 6c 6d 6e 6f 70 71 72 73 74 77 7a 32 33 34 35 klmnopqrstwz2345
7b60: 36 37 41 44 22 2c 20 22 72 65 63 65 69 70 74 22 67AD", "receipt"
7b70: 3a 20 32 2c 20 22 72 69 64 22 3a 20 32 7d 5d 2c : 2, "rid": 2}],
7b80: 20 22 75 73 65 72 22 3a 20 7b 22 75 73 65 72 22 "user": {"user"
7b90: 3a 20 22 64 65 6d 6f 2d 6e 6f 64 65 22 2c 20 22 : "demo-node", "
7ba0: 63 61 70 61 62 69 6c 69 74 69 65 73 22 3a 20 22 capabilities": "
7bb0: 67 68 6a 6f 72 77 7a 34 43 22 2c 20 22 77 69 6b ghjorwz4C", "wik
7bc0: 69 5f 6d 6f 64 65 72 61 74 65 64 22 3a 20 66 61 i_moderated": fa
7bd0: 6c 73 65 2c 20 22 74 69 63 6b 65 74 5f 6d 6f 64 lse, "ticket_mod
7be0: 65 72 61 74 65 64 22 3a 20 66 61 6c 73 65 7d 7d erated": false}}
7bf0: 0a 46 6f 73 73 69 6c 27 73 20 6f 77 6e 20 72 65 .Fossil's own re
7c00: 63 6f 72 64 3a 20 27 62 6f 73 73 27 2c 27 62 63 cord: 'boss','bc
7c10: 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 defghijklmnopqrs
7c20: 74 77 7a 32 33 34 35 36 37 41 44 27 2c 32 2c 32 twz234567AD',2,2
7c30: 0a 46 6f 73 73 69 6c 27 73 20 66 75 6c 6c 63 61 .Fossil's fullca
7c40: 70 28 64 65 6d 6f 2d 6e 6f 64 65 29 3a 20 27 67 p(demo-node): 'g
7c50: 68 6a 6f 72 77 7a 34 43 27 0a 0a 3d 3d 20 74 68 hjorwz4C'..== th
7c60: 65 20 63 6f 6e 73 6f 6c 65 2c 20 61 73 20 62 6f e console, as bo
7c70: 73 73 20 28 74 68 65 20 70 61 67 65 27 73 20 6f ss (the page's o
7c80: 74 68 65 72 20 70 61 74 68 73 20 73 74 69 6c 6c ther paths still
7c90: 20 70 61 73 73 20 74 68 72 6f 75 67 68 3a 20 45 pass through: E
7ca0: 36 29 0a 63 6f 6e 73 6f 6c 65 20 6f 6b 3a 20 54 6).console ok: T
7cb0: 72 75 65 20 7c 20 72 75 6e 73 20 72 75 6e 6e 69 rue | runs runni
7cc0: 6e 67 3a 20 30 20 7c 20 65 6e 64 65 64 3a 20 32 ng: 0 | ended: 2
7cd0: 0a 0a 3d 3d 20 77 72 69 74 65 73 20 66 72 6f 6d ..== writes from
7ce0: 20 74 68 65 20 72 65 63 65 69 70 74 20 73 65 72 the receipt ser
7cf0: 76 69 63 65 27 73 20 70 6f 6c 69 63 79 20 66 61 vice's policy fa
7d00: 69 6c 20 28 61 20 70 72 6f 62 65 20 69 6e 20 70 il (a probe in p
7d10: 6c 61 63 65 20 6f 66 20 72 65 63 65 69 70 74 73 lace of receipts
7d20: 29 0a 46 6f 73 73 69 6c 27 73 20 70 6f 72 74 20 ).Fossil's port
7d30: 34 35 39 34 35 20 61 6e 73 77 65 72 73 20 6f 6e 45945 answers on
7d40: 20 74 68 65 20 68 6f 73 74 3a 20 32 30 30 0a 47 the host: 200.G
7d50: 43 20 57 61 72 6e 69 6e 67 3a 20 43 6f 75 6c 64 C Warning: Could
7d60: 20 6e 6f 74 20 6f 70 65 6e 20 2f 70 72 6f 63 2f not open /proc/
7d70: 73 74 61 74 0a 6f 70 65 6e 20 74 68 65 20 72 65 stat.open the re
7d80: 70 6f 73 69 74 6f 72 79 20 66 6f 72 20 77 72 69 pository for wri
7d90: 74 69 6e 67 3a 20 72 65 66 75 73 65 64 20 28 46 ting: refused (F
7da0: 69 6c 65 3a 3a 45 72 72 6f 72 3a 20 45 72 72 6f ile::Error: Erro
7db0: 72 20 6f 70 65 6e 69 6e 67 20 66 69 6c 65 20 77 r opening file w
7dc0: 69 74 68 20 6d 6f 64 65 20 27 72 2b 27 3a 20 27 ith mode 'r+': '
7dd0: 2f 74 6d 70 2f 73 41 2d 33 37 30 37 34 35 32 2f /tmp/sA-3707452/
7de0: 66 6f 73 73 69 6c 2f 64 65 6d 6f 2e 66 6f 73 73 fossil/demo.foss
7df0: 69 6c 27 3a 20 52 65 61 64 2d 6f 6e 6c 79 20 66 il': Read-only f
7e00: 69 6c 65 20 73 79 73 74 65 6d 29 0a 63 72 65 61 ile system).crea
7e10: 74 65 20 61 20 66 69 6c 65 20 62 65 73 69 64 65 te a file beside
7e20: 20 69 74 3a 20 72 65 66 75 73 65 64 20 28 46 69 it: refused (Fi
7e30: 6c 65 3a 3a 45 72 72 6f 72 3a 20 45 72 72 6f 72 le::Error: Error
7e40: 20 6f 70 65 6e 69 6e 67 20 66 69 6c 65 20 77 69 opening file wi
7e50: 74 68 20 6d 6f 64 65 20 27 77 27 3a 20 27 2f 74 th mode 'w': '/t
7e60: 6d 70 2f 73 41 2d 33 37 30 37 34 35 32 2f 66 6f mp/sA-3707452/fo
7e70: 73 73 69 6c 2f 78 27 3a 20 52 65 61 64 2d 6f 6e ssil/x': Read-on
7e80: 6c 79 20 66 69 6c 65 20 73 79 73 74 65 6d 29 0a ly file system).
7e90: 72 65 61 64 20 74 68 65 20 72 65 70 6f 73 69 74 read the reposit
7ea0: 6f 72 79 3a 20 72 65 61 64 20 6f 6b 20 28 61 6c ory: read ok (al
7eb0: 6c 6f 77 65 64 3a 20 72 65 61 64 2d 6f 6e 6c 79 lowed: read-only
7ec0: 29 0a 72 65 61 63 68 20 46 6f 73 73 69 6c 27 73 ).reach Fossil's
7ed0: 20 70 6f 72 74 3a 20 72 65 66 75 73 65 64 20 28 port: refused (
7ee0: 53 6f 63 6b 65 74 3a 3a 43 6f 6e 6e 65 63 74 45 Socket::ConnectE
7ef0: 72 72 6f 72 3a 20 45 72 72 6f 72 20 63 6f 6e 6e rror: Error conn
7f00: 65 63 74 69 6e 67 20 74 6f 20 27 31 32 37 2e 30 ecting to '127.0
7f10: 2e 30 2e 31 3a 34 35 39 34 35 27 3a 20 43 6f 6e .0.1:45945': Con
7f20: 6e 65 63 74 69 6f 6e 20 72 65 66 75 73 65 64 29 nection refused)
7f30: 0a 77 72 69 74 65 20 69 6e 20 69 74 73 20 73 6f .write in its so
7f40: 63 6b 65 74 20 64 69 72 65 63 74 6f 72 79 3a 20 cket directory:
7f50: 77 72 69 74 74 65 6e 20 28 61 6c 6c 6f 77 65 64 written (allowed
7f60: 3a 20 69 74 73 20 6f 6e 65 20 77 72 69 74 61 62 : its one writab
7f70: 6c 65 20 70 6c 61 63 65 29 0a 0a 3d 3d 20 74 68 le place)..== th
7f80: 65 20 64 61 65 6d 6f 6e 27 73 20 6f 77 6e 20 76 e daemon's own v
7f90: 69 65 77 3a 20 61 20 70 72 6f 62 65 20 69 6e 20 iew: a probe in
7fa0: 70 6c 61 63 65 20 6f 66 20 69 74 73 20 70 72 6f place of its pro
7fb0: 67 72 61 6d 2c 20 73 74 61 72 74 65 64 20 74 68 gram, started th
7fc0: 72 6f 75 67 68 20 74 68 65 20 73 61 6d 65 20 64 rough the same d
7fd0: 61 65 6d 6f 6e 20 70 6f 6c 69 63 79 0a 64 61 65 aemon policy.dae
7fe0: 6d 6f 6e 20 70 72 6f 62 65 3a 20 75 69 64 3d 30 mon probe: uid=0
7ff0: 20 63 61 70 73 3d 43 61 70 45 66 66 3a 20 30 30 caps=CapEff: 00
8000: 30 30 30 31 66 66 66 66 66 66 66 66 66 66 0a 61 0001ffffffffff.a
8010: 62 73 65 6e 74 20 2f 74 6d 70 2f 73 41 2d 33 37 bsent /tmp/sA-37
8020: 30 37 34 35 32 2f 66 6f 73 73 69 6c 0a 61 62 73 07452/fossil.abs
8030: 65 6e 74 20 2f 74 6d 70 2f 73 41 2d 33 37 30 37 ent /tmp/sA-3707
8040: 34 35 32 2f 66 6f 73 73 69 6c 2f 64 65 6d 6f 2e 452/fossil/demo.
8050: 66 6f 73 73 69 6c 0a 70 72 6f 63 2d 70 61 74 68 fossil.proc-path
8060: 73 2d 72 65 61 64 61 62 6c 65 3d 30 20 28 6f 74 s-readable=0 (ot
8070: 68 65 72 20 70 72 6f 63 65 73 73 65 73 20 6f 66 her processes of
8080: 20 74 68 65 20 73 61 6d 65 20 61 63 63 6f 75 6e the same accoun
8090: 74 3b 20 74 68 65 20 6c 69 76 65 20 64 61 65 6d t; the live daem
80a0: 6f 6e 20 68 61 73 20 61 6e 20 61 63 63 6f 75 6e on has an accoun
80b0: 74 20 6f 66 20 69 74 73 20 6f 77 6e 29 0a 61 67 t of its own).ag
80c0: 65 6e 74 20 73 61 6e 64 62 6f 78 20 69 6e 73 69 ent sandbox insi
80d0: 64 65 3a 20 73 74 61 72 74 65 64 0a 0a 3d 3d 20 de: started..==
80e0: 72 65 76 69 65 77 20 74 65 73 74 3a 20 74 68 65 review test: the
80f0: 20 2f 72 65 63 65 69 70 74 20 61 6c 69 61 73 65 /receipt aliase
8100: 73 20 72 65 61 63 68 20 74 68 65 20 72 65 63 65 s reach the rece
8110: 69 70 74 20 73 65 72 76 69 63 65 2c 20 6e 65 76 ipt service, nev
8120: 65 72 20 74 68 65 20 70 61 73 73 74 68 72 6f 75 er the passthrou
8130: 67 68 0a 2f 72 65 63 65 69 70 74 20 2d 3e 20 73 gh./receipt -> s
8140: 65 72 76 69 63 65 20 62 6f 73 73 0a 2f 72 65 63 ervice boss./rec
8150: 65 69 70 74 2f 20 2d 3e 20 73 65 72 76 69 63 65 eipt/ -> service
8160: 20 62 6f 73 73 0a 2f 72 65 63 65 69 70 74 2f 78 boss./receipt/x
8170: 20 2d 3e 20 73 65 72 76 69 63 65 20 62 6f 73 73 -> service boss
8180: 0a 0a 3d 3d 20 72 65 76 69 65 77 20 74 65 73 74 ..== review test
8190: 3a 20 6f 6e 65 20 72 65 70 6f 73 69 74 6f 72 79 : one repository
81a0: 20 68 65 6c 64 20 6c 6f 63 6b 65 64 20 77 68 69 held locked whi
81b0: 6c 65 20 61 6e 6f 74 68 65 72 20 69 73 20 61 73 le another is as
81c0: 6b 65 64 2c 20 73 74 72 61 69 67 68 74 20 61 74 ked, straight at
81d0: 20 74 68 65 20 73 65 72 76 69 63 65 27 73 20 73 the service's s
81e0: 6f 63 6b 65 74 0a 64 65 6d 6f 20 28 6c 6f 63 6b ocket.demo (lock
81f0: 65 64 3d 54 72 75 65 29 3a 20 32 2e 30 20 73 20 ed=True): 2.0 s
8200: 20 7b 22 75 6e 61 76 61 69 6c 61 62 6c 65 22 3a {"unavailable":
8210: 22 53 51 4c 49 54 45 5f 42 55 53 59 20 28 35 29 "SQLITE_BUSY (5)
8220: 3a 20 64 61 74 61 62 61 73 65 20 69 73 20 6c 6f : database is lo
8230: 63 6b 65 64 22 2c 22 72 65 74 72 79 5f 61 66 74 cked","retry_aft
8240: 65 72 22 3a 31 7d 0a 64 65 6d 6f 32 20 28 6c 6f er":1}.demo2 (lo
8250: 63 6b 65 64 3d 46 61 6c 73 65 29 3a 20 30 2e 30 cked=False): 0.0
8260: 20 73 20 20 7b 22 61 72 74 69 66 61 63 74 73 22 s {"artifacts"
8270: 3a 5b 7b 22 61 72 74 69 66 61 63 74 22 3a 22 64 :[{"artifact":"d
8280: 30 39 38 39 65 63 64 62 32 65 36 32 63 63 61 62 0989ecdb2e62ccab
8290: 66 34 65 38 64 33 62 62 62 61 62 32 65 31 37 37 f4e8d3bbbab2e177
82a0: 31 64 34 34 66 32 64 63 36 39 32 38 31 31 62 36 1d44f2dc692811b6
82b0: 64 66 30 34 62 66 30 31 33 32 35 61 32 38 0a 0a df04bf01325a28..
82c0: 3d 3d 20 72 65 76 69 65 77 20 74 65 73 74 3a 20 == review test:
82d0: 69 6e 73 69 64 65 20 74 68 65 20 43 47 49 20 73 inside the CGI s
82e0: 61 6e 64 62 6f 78 20 28 61 20 70 72 6f 62 65 20 andbox (a probe
82f0: 69 6e 20 70 6c 61 63 65 20 6f 66 20 74 68 65 20 in place of the
8300: 72 65 6c 65 61 73 65 27 73 20 70 72 6f 67 72 61 release's progra
8310: 6d 29 0a 61 72 67 73 3a 20 2d 2d 72 65 63 65 69 m).args: --recei
8320: 70 74 2d 66 64 20 33 0a 43 61 70 45 66 66 3a 20 pt-fd 3.CapEff:
8330: 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
8340: 0a 66 64 20 30 20 2d 3e 20 73 6f 63 6b 65 74 3a .fd 0 -> socket:
8350: 5b 39 38 36 36 34 33 36 31 33 5d 0a 66 64 20 31 [986643613].fd 1
8360: 20 2d 3e 20 70 69 70 65 3a 5b 39 38 36 36 34 36 -> pipe:[986646
8370: 37 30 35 5d 0a 66 64 20 31 30 20 2d 3e 20 2f 72 705].fd 10 -> /r
8380: 65 6c 65 61 73 65 2f 6f 63 78 32 2d 63 67 69 0a elease/ocx2-cgi.
8390: 66 64 20 32 20 2d 3e 20 70 69 70 65 3a 5b 39 38 fd 2 -> pipe:[98
83a0: 36 36 34 36 37 30 35 5d 0a 66 64 20 33 20 2d 3e 6646705].fd 3 ->
83b0: 20 73 6f 63 6b 65 74 3a 5b 39 38 36 36 34 39 36 socket:[9866496
83c0: 39 33 5d 0a 66 64 20 34 20 2d 3e 20 70 69 70 65 93].fd 4 -> pipe
83d0: 3a 5b 39 38 36 36 34 34 30 32 32 5d 0a 63 6f 6f :[986644022].coo
83e0: 6b 69 65 3a 20 61 62 73 65 6e 74 20 6e 6f 6e 63 kie: absent nonc
83f0: 65 3a 20 61 62 73 65 6e 74 20 72 65 70 6f 73 69 e: absent reposi
8400: 74 6f 72 79 3a 20 64 65 6d 6f 0a 61 62 73 65 6e tory: demo.absen
8410: 74 20 2f 74 6d 70 2f 73 41 2d 33 37 30 37 34 35 t /tmp/sA-370745
8420: 32 2f 66 6f 73 73 69 6c 0a 61 62 73 65 6e 74 20 2/fossil.absent
8430: 2f 74 6d 70 2f 73 41 2d 33 37 30 37 34 35 32 2f /tmp/sA-3707452/
8440: 66 6f 73 73 69 6c 2f 64 65 6d 6f 2e 66 6f 73 73 fossil/demo.foss
8450: 69 6c 0a 6e 65 74 77 6f 72 6b 3a 20 30 30 30 0a il.network: 000.
8460: 0a 3d 3d 20 72 65 76 69 65 77 20 74 65 73 74 3a .== review test:
8470: 20 61 20 72 65 63 65 69 70 74 20 73 65 72 76 69 a receipt servi
8480: 63 65 20 74 68 61 74 20 6c 69 73 74 65 6e 73 20 ce that listens
8490: 62 75 74 20 6e 65 76 65 72 20 61 63 63 65 70 74 but never accept
84a0: 73 0a 73 74 61 74 75 73 20 35 30 33 20 61 66 74 s.status 503 aft
84b0: 65 72 20 32 20 73 3a 20 7b 22 75 6e 61 76 61 69 er 2 s: {"unavai
84c0: 6c 61 62 6c 65 22 3a 22 72 65 63 65 69 70 74 20 lable":"receipt
84d0: 73 65 72 76 69 63 65 20 6e 6f 74 20 61 6e 73 77 service not answ
84e0: 65 72 69 6e 67 22 7d 0a 0a 3d 3d 20 72 6f 6c 6c ering"}..== roll
84f0: 62 61 63 6b 20 70 6f 69 6e 74 3a 20 74 68 65 20 back point: the
8500: 6c 61 75 6e 63 68 65 72 27 73 20 43 47 49 20 6c launcher's CGI l
8510: 69 6e 6b 20 69 73 20 6f 6e 65 20 66 69 6c 65 3b ink is one file;
8520: 20 74 68 65 20 70 72 65 76 69 6f 75 73 20 70 61 the previous pa
8530: 67 65 20 69 73 20 74 68 65 20 72 65 6c 65 61 73 ge is the releas
8540: 65 27 73 20 6f 77 6e 20 6f 63 78 32 0a 2d 72 77 e's own ocx2.-rw
8550: 78 72 2d 78 72 2d 78 20 20 20 20 31 20 72 6f 6f xr-xr-x 1 roo
8560: 74 20 20 20 20 20 72 6f 6f 74 20 20 20 20 20 20 t root
8570: 20 20 20 20 20 31 39 37 20 4f 63 74 20 32 39 20 197 Oct 29
8580: 30 31 3a 34 38 20 2f 74 6d 70 2f 73 41 2d 33 37 01:48 /tmp/sA-37
8590: 30 37 34 35 32 2f 65 78 74 2f 6f 63 78 32 2d 63 07452/ext/ocx2-c
85a0: 67 69 0a 64 6f 6e 65 0a 00 00 00 00 00 00 00 00 gi.done.........
85b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
85c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
85d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
85e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
85f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8600: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69 scripts-for-revi
8610: 65 77 2d 32 2f 73 74 61 67 65 41 2d 64 65 6d 6f ew-2/stageA-demo
8620: 2e 73 68 00 00 00 00 00 00 00 00 00 00 00 00 00 .sh.............
8630: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8640: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8650: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8660: 00 00 00 00 30 30 30 30 37 35 35 00 30 30 30 30 ....0000755.0000
8670: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 000.0000000.0000
8680: 30 30 33 32 36 32 32 00 31 35 32 37 30 35 32 33 0032622.15270523
8690: 31 32 32 00 30 31 36 31 35 36 00 20 30 00 00 00 122.016156. 0...
86a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
86b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
86c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
86d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
86e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
86f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8700: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00 .ustar .root...
8710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8720: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00 .........root...
8730: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8740: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8750: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8760: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8790: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
87a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
87b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
87c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
87d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
87e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
87f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8800: 23 21 2f 62 69 6e 2f 62 61 73 68 0a 23 20 53 74 #!/bin/bash.# St
8810: 61 67 65 20 41 20 6f 6e 20 61 20 73 63 72 61 74 age A on a scrat
8820: 63 68 20 64 65 70 6c 6f 79 6d 65 6e 74 3a 20 74 ch deployment: t
8830: 68 65 20 72 65 63 65 69 70 74 20 73 65 72 76 69 he receipt servi
8840: 63 65 2c 20 74 68 65 20 6c 61 75 6e 63 68 65 72 ce, the launcher
8850: 27 73 20 43 47 49 0a 23 20 72 6f 75 74 65 20 61 's CGI.# route a
8860: 6e 64 20 74 68 65 20 63 6f 6e 66 69 6e 65 64 20 nd the confined
8870: 64 61 65 6d 6f 6e 2c 20 77 69 74 68 20 73 74 6f daemon, with sto
8880: 63 6b 20 46 6f 73 73 69 6c 20 32 2e 32 37 2e 20 ck Fossil 2.27.
8890: 55 73 61 67 65 3a 0a 23 20 20 20 73 74 61 67 65 Usage:.# stage
88a0: 41 2d 64 65 6d 6f 2e 73 68 20 54 52 45 45 20 4f A-demo.sh TREE O
88b0: 55 54 0a 23 20 54 52 45 45 20 69 73 20 74 68 65 UT.# TREE is the
88c0: 20 63 68 65 63 6b 6f 75 74 20 77 68 6f 73 65 20 checkout whose
88d0: 62 69 6e 2f 20 68 6f 6c 64 73 20 73 74 61 72 74 bin/ holds start
88e0: 2d 6f 63 78 2c 20 72 65 63 65 69 70 74 73 20 61 -ocx, receipts a
88f0: 6e 64 20 6f 63 78 32 3b 20 4f 55 54 0a 23 20 69 nd ocx2; OUT.# i
8900: 73 20 61 20 6e 65 77 20 64 69 72 65 63 74 6f 72 s a new director
8910: 79 20 66 6f 72 20 74 68 65 20 73 63 72 61 74 63 y for the scratc
8920: 68 20 64 65 70 6c 6f 79 6d 65 6e 74 20 61 6e 64 h deployment and
8930: 20 69 74 73 20 72 65 63 6f 72 64 2e 20 45 76 65 its record. Eve
8940: 72 79 0a 23 20 70 72 6f 63 65 73 73 20 69 74 20 ry.# process it
8950: 73 74 61 72 74 73 20 69 73 20 74 69 6d 65 2d 6c starts is time-l
8960: 69 6d 69 74 65 64 2c 20 61 6e 64 20 69 74 20 73 imited, and it s
8970: 74 6f 70 73 20 74 68 65 6d 20 61 6c 6c 20 61 74 tops them all at
8980: 20 74 68 65 20 65 6e 64 2e 0a 73 65 74 20 2d 75 the end..set -u
8990: 0a 54 52 45 45 3d 24 31 3b 20 4f 55 54 3d 24 32 .TREE=$1; OUT=$2
89a0: 0a 5b 20 2d 65 20 22 24 4f 55 54 22 20 5d 20 26 .[ -e "$OUT" ] &
89b0: 26 20 7b 20 65 63 68 6f 20 22 24 4f 55 54 20 65 & { echo "$OUT e
89c0: 78 69 73 74 73 22 3b 20 65 78 69 74 20 32 3b 20 xists"; exit 2;
89d0: 7d 0a 6d 6b 64 69 72 20 2d 70 20 22 24 4f 55 54 }.mkdir -p "$OUT
89e0: 22 3b 20 4f 55 54 3d 24 28 63 64 20 22 24 4f 55 "; OUT=$(cd "$OU
89f0: 54 22 20 26 26 20 70 77 64 29 0a 23 20 61 20 73 T" && pwd).# a s
8a00: 68 6f 72 74 20 70 61 74 68 3a 20 61 20 55 6e 69 hort path: a Uni
8a10: 78 20 73 6f 63 6b 65 74 27 73 20 70 61 74 68 20 x socket's path
8a20: 6d 61 79 20 62 65 20 61 74 20 6d 6f 73 74 20 31 may be at most 1
8a30: 30 37 20 62 79 74 65 73 0a 44 3d 2f 74 6d 70 2f 07 bytes.D=/tmp/
8a40: 73 41 2d 24 24 3b 20 4c 4f 47 3d 24 4f 55 54 2f sA-$$; LOG=$OUT/
8a50: 72 65 63 6f 72 64 2e 74 78 74 3b 20 72 6d 20 2d record.txt; rm -
8a60: 72 66 20 24 44 0a 6d 6b 64 69 72 20 2d 70 20 24 rf $D.mkdir -p $
8a70: 44 2f 66 6f 73 73 69 6c 20 24 44 2f 72 75 6e 20 D/fossil $D/run
8a80: 24 44 2f 72 65 6c 65 61 73 65 20 24 44 2f 64 61 $D/release $D/da
8a90: 65 6d 6f 6e 2f 6e 6f 64 65 20 24 44 2f 68 6f 6d emon/node $D/hom
8aa0: 65 2f 2e 63 6f 64 65 78 20 24 44 2f 65 78 74 0a e/.codex $D/ext.
8ab0: 65 78 65 63 20 3e 20 3e 28 74 65 65 20 2d 61 20 exec > >(tee -a
8ac0: 24 4c 4f 47 29 20 32 3e 26 31 0a 73 61 79 28 29 $LOG) 2>&1.say()
8ad0: 20 7b 20 65 63 68 6f 3b 20 65 63 68 6f 20 22 3d { echo; echo "=
8ae0: 3d 20 24 2a 22 3b 20 7d 0a 70 69 64 73 3d 28 29 = $*"; }.pids=()
8af0: 0a 63 6c 65 61 6e 75 70 28 29 20 7b 20 66 6f 72 .cleanup() { for
8b00: 20 70 20 69 6e 20 22 24 7b 70 69 64 73 5b 40 5d p in "${pids[@]
8b10: 7d 22 3b 20 64 6f 20 6b 69 6c 6c 20 24 70 20 32 }"; do kill $p 2
8b20: 3e 2f 64 65 76 2f 6e 75 6c 6c 3b 20 64 6f 6e 65 >/dev/null; done
8b30: 3b 20 77 61 69 74 20 32 3e 2f 64 65 76 2f 6e 75 ; wait 2>/dev/nu
8b40: 6c 6c 3b 20 7d 0a 74 72 61 70 20 27 63 6c 65 61 ll; }.trap 'clea
8b50: 6e 75 70 3b 20 72 6d 20 2d 72 66 20 24 44 27 20 nup; rm -rf $D'
8b60: 45 58 49 54 0a 0a 73 61 79 20 22 62 75 69 6c 64 EXIT..say "build
8b70: 3a 20 73 74 61 72 74 2d 6f 63 78 2c 20 72 65 63 : start-ocx, rec
8b80: 65 69 70 74 73 20 61 6e 64 20 6f 63 78 32 20 66 eipts and ocx2 f
8b90: 72 6f 6d 20 24 54 52 45 45 22 0a 42 49 4e 3d 24 rom $TREE".BIN=$
8ba0: 4f 55 54 2f 62 69 6e 3b 20 6d 6b 64 69 72 20 2d OUT/bin; mkdir -
8bb0: 70 20 24 42 49 4e 0a 66 6f 72 20 74 20 69 6e 20 p $BIN.for t in
8bc0: 73 74 61 72 74 2d 6f 63 78 20 72 65 63 65 69 70 start-ocx receip
8bd0: 74 73 3b 20 64 6f 20 6e 69 63 65 20 2d 6e 20 31 ts; do nice -n 1
8be0: 39 20 63 72 79 73 74 61 6c 20 62 75 69 6c 64 20 9 crystal build
8bf0: 2d 2d 72 65 6c 65 61 73 65 20 2d 6f 20 24 42 49 --release -o $BI
8c00: 4e 2f 24 74 20 24 54 52 45 45 2f 73 63 72 69 70 N/$t $TREE/scrip
8c10: 74 73 2f 24 74 2e 63 72 20 7c 7c 20 65 78 69 74 ts/$t.cr || exit
8c20: 20 31 3b 20 64 6f 6e 65 0a 63 70 20 24 54 52 45 1; done.cp $TRE
8c30: 45 2f 62 69 6e 2f 6f 63 78 32 20 24 42 49 4e 2f E/bin/ocx2 $BIN/
8c40: 6f 63 78 32 20 7c 7c 20 65 78 69 74 20 31 0a 52 ocx2 || exit 1.R
8c50: 45 4c 3d 64 65 6d 6f 30 30 30 30 30 30 30 31 0a EL=demo00000001.
8c60: 66 6f 72 20 72 20 69 6e 20 24 44 2f 72 65 6c 65 for r in $D/rele
8c70: 61 73 65 20 24 44 2f 64 61 65 6d 6f 6e 3b 20 64 ase $D/daemon; d
8c80: 6f 20 6d 6b 64 69 72 20 2d 70 20 24 72 2f 24 52 o mkdir -p $r/$R
8c90: 45 4c 20 24 72 2f 73 74 61 67 65 64 3b 20 63 70 EL $r/staged; cp
8ca0: 20 24 42 49 4e 2f 6f 63 78 32 20 24 72 2f 24 52 $BIN/ocx2 $r/$R
8cb0: 45 4c 2f 6f 63 78 32 3b 20 6c 6e 20 2d 73 20 24 EL/ocx2; ln -s $
8cc0: 52 45 4c 20 24 72 2f 63 75 72 72 65 6e 74 3b 20 REL $r/current;
8cd0: 64 6f 6e 65 0a 0a 73 61 79 20 22 73 65 72 76 65 done..say "serve
8ce0: 64 20 72 65 70 6f 73 69 74 6f 72 79 3a 20 64 65 d repository: de
8cf0: 6d 6f 2e 66 6f 73 73 69 6c 2c 20 75 73 65 72 73 mo.fossil, users
8d00: 2c 20 63 68 61 74 20 74 69 6d 65 6c 69 6e 65 22 , chat timeline"
8d10: 0a 46 3d 2f 75 73 72 2f 62 69 6e 2f 66 6f 73 73 .F=/usr/bin/foss
8d20: 69 6c 3b 20 52 3d 24 44 2f 66 6f 73 73 69 6c 2f il; R=$D/fossil/
8d30: 64 65 6d 6f 2e 66 6f 73 73 69 6c 0a 65 78 70 6f demo.fossil.expo
8d40: 72 74 20 48 4f 4d 45 3d 24 4f 55 54 2f 61 64 6d rt HOME=$OUT/adm
8d50: 69 6e 68 6f 6d 65 3b 20 6d 6b 64 69 72 20 2d 70 inhome; mkdir -p
8d60: 20 24 48 4f 4d 45 0a 24 46 20 6e 65 77 20 24 52 $HOME.$F new $R
8d70: 20 2d 2d 61 64 6d 69 6e 2d 75 73 65 72 20 62 6f --admin-user bo
8d80: 73 73 20 3e 2f 64 65 76 2f 6e 75 6c 6c 0a 24 46 ss >/dev/null.$F
8d90: 20 75 73 65 72 20 70 61 73 73 77 6f 72 64 20 62 user password b
8da0: 6f 73 73 20 62 6f 73 73 2d 70 77 20 2d 52 20 24 oss boss-pw -R $
8db0: 52 0a 66 6f 72 20 75 20 69 6e 20 22 64 65 6d 6f R.for u in "demo
8dc0: 2d 6e 6f 64 65 20 67 6f 43 34 77 20 6e 6f 64 65 -node goC4w node
8dd0: 2d 70 77 22 20 22 62 75 69 6c 64 65 72 20 67 69 -pw" "builder gi
8de0: 6f 32 34 20 62 75 69 6c 64 65 72 2d 70 77 22 20 o24 builder-pw"
8df0: 22 76 69 65 77 65 72 20 6f 32 20 76 69 65 77 65 "viewer o2 viewe
8e00: 72 2d 70 77 22 3b 20 64 6f 20 73 65 74 20 2d 2d r-pw"; do set --
8e10: 20 24 75 0a 20 20 24 46 20 75 73 65 72 20 6e 65 $u. $F user ne
8e20: 77 20 24 31 20 24 31 40 78 20 24 33 20 2d 52 20 w $1 $1@x $3 -R
8e30: 24 52 20 3e 2f 64 65 76 2f 6e 75 6c 6c 3b 20 24 $R >/dev/null; $
8e40: 46 20 75 73 65 72 20 63 61 70 61 62 69 6c 69 74 F user capabilit
8e50: 69 65 73 20 24 31 20 24 32 20 2d 52 20 24 52 20 ies $1 $2 -R $R
8e60: 3e 2f 64 65 76 2f 6e 75 6c 6c 3b 20 64 6f 6e 65 >/dev/null; done
8e70: 0a 24 46 20 73 65 74 74 69 6e 67 73 20 63 68 61 .$F settings cha
8e80: 74 2d 74 69 6d 65 6c 69 6e 65 2d 75 73 65 72 20 t-timeline-user
8e90: 6f 63 78 2d 74 69 6d 65 6c 69 6e 65 20 2d 52 20 ocx-timeline -R
8ea0: 24 52 0a 24 46 20 73 65 74 74 69 6e 67 73 20 63 $R.$F settings c
8eb0: 68 61 74 2d 70 6f 6c 6c 2d 74 69 6d 65 6f 75 74 hat-poll-timeout
8ec0: 20 33 20 2d 52 20 24 52 0a 63 68 6d 6f 64 20 36 3 -R $R.chmod 6
8ed0: 30 30 20 24 52 3b 20 6c 73 20 2d 6c 20 24 52 0a 00 $R; ls -l $R.
8ee0: 0a 73 61 79 20 22 72 65 63 65 69 70 74 20 73 65 .say "receipt se
8ef0: 72 76 69 63 65 2c 20 74 68 72 6f 75 67 68 20 74 rvice, through t
8f00: 68 65 20 6c 61 75 6e 63 68 65 72 27 73 20 72 65 he launcher's re
8f10: 63 65 69 70 74 73 20 70 6f 6c 69 63 79 22 0a 74 ceipts policy".t
8f20: 69 6d 65 6f 75 74 20 39 30 30 20 24 42 49 4e 2f imeout 900 $BIN/
8f30: 73 74 61 72 74 2d 6f 63 78 20 2d 2d 73 63 72 61 start-ocx --scra
8f40: 74 63 68 20 24 44 20 72 65 63 65 69 70 74 73 20 tch $D receipts
8f50: 32 3e 24 4f 55 54 2f 72 65 63 65 69 70 74 73 2e 2>$OUT/receipts.
8f60: 65 72 72 20 26 20 70 69 64 73 2b 3d 28 24 21 29 err & pids+=($!)
8f70: 0a 73 6c 65 65 70 20 31 3b 20 6c 73 20 2d 6c 20 .sleep 1; ls -l
8f80: 24 44 2f 72 75 6e 2f 0a 0a 73 61 79 20 22 46 6f $D/run/..say "Fo
8f90: 73 73 69 6c 20 73 65 72 76 65 72 2c 20 69 74 73 ssil server, its
8fa0: 20 65 78 74 72 6f 6f 74 27 73 20 6f 63 78 32 2d extroot's ocx2-
8fb0: 63 67 69 20 74 68 65 20 6c 61 75 6e 63 68 65 72 cgi the launcher
8fc0: 22 0a 70 72 69 6e 74 66 20 27 23 21 2f 62 69 6e ".printf '#!/bin
8fd0: 2f 62 61 73 68 5c 6e 65 78 65 63 20 2d 61 20 6f /bash\nexec -a o
8fe0: 63 78 32 2d 63 67 69 20 25 73 20 2d 2d 73 63 72 cx2-cgi %s --scr
8ff0: 61 74 63 68 20 25 73 5c 6e 27 20 24 42 49 4e 2f atch %s\n' $BIN/
9000: 73 74 61 72 74 2d 6f 63 78 20 24 44 20 3e 20 24 start-ocx $D > $
9010: 44 2f 65 78 74 2f 6f 63 78 32 2d 63 67 69 3b 20 D/ext/ocx2-cgi;
9020: 63 68 6d 6f 64 20 37 35 35 20 24 44 2f 65 78 74 chmod 755 $D/ext
9030: 2f 6f 63 78 32 2d 63 67 69 0a 50 3d 24 28 70 79 /ocx2-cgi.P=$(py
9040: 74 68 6f 6e 33 20 2d 63 20 27 69 6d 70 6f 72 74 thon3 -c 'import
9050: 20 73 6f 63 6b 65 74 3b 73 3d 73 6f 63 6b 65 74 socket;s=socket
9060: 2e 73 6f 63 6b 65 74 28 29 3b 73 2e 62 69 6e 64 .socket();s.bind
9070: 28 28 22 31 32 37 2e 30 2e 30 2e 31 22 2c 30 29 (("127.0.0.1",0)
9080: 29 3b 70 72 69 6e 74 28 73 2e 67 65 74 73 6f 63 );print(s.getsoc
9090: 6b 6e 61 6d 65 28 29 5b 31 5d 29 27 29 0a 55 3d kname()[1])').U=
90a0: 68 74 74 70 3a 2f 2f 31 32 37 2e 30 2e 30 2e 31 http://127.0.0.1
90b0: 3a 24 50 2f 64 65 6d 6f 0a 74 69 6d 65 6f 75 74 :$P/demo.timeout
90c0: 20 39 30 30 20 24 46 20 73 65 72 76 65 72 20 2d 900 $F server -
90d0: 2d 70 6f 72 74 20 24 50 20 2d 2d 6c 6f 63 61 6c -port $P --local
90e0: 68 6f 73 74 20 2d 2d 6e 6f 6a 61 69 6c 20 2d 2d host --nojail --
90f0: 72 65 70 6f 6c 69 73 74 20 24 44 2f 66 6f 73 73 repolist $D/foss
9100: 69 6c 20 2d 2d 65 78 74 72 6f 6f 74 20 24 44 2f il --extroot $D/
9110: 65 78 74 20 3e 24 4f 55 54 2f 66 6f 73 73 69 6c ext >$OUT/fossil
9120: 2e 6c 6f 67 20 32 3e 26 31 20 26 20 70 69 64 73 .log 2>&1 & pids
9130: 2b 3d 28 24 21 29 0a 66 6f 72 20 69 20 69 6e 20 +=($!).for i in
9140: 24 28 73 65 71 20 35 30 29 3b 20 64 6f 20 63 75 $(seq 50); do cu
9150: 72 6c 20 2d 73 20 2d 6f 20 2f 64 65 76 2f 6e 75 rl -s -o /dev/nu
9160: 6c 6c 20 68 74 74 70 3a 2f 2f 31 32 37 2e 30 2e ll http://127.0.
9170: 30 2e 31 3a 24 50 2f 20 26 26 20 62 72 65 61 6b 0.1:$P/ && break
9180: 3b 20 73 6c 65 65 70 20 30 2e 32 3b 20 64 6f 6e ; sleep 0.2; don
9190: 65 0a 0a 73 61 79 20 22 74 68 65 20 64 61 65 6d e..say "the daem
91a0: 6f 6e 27 73 20 6e 6f 64 65 3a 20 6e 6f 64 65 73 on's node: nodes
91b0: 2e 6a 73 6f 6e 20 61 6e 64 20 63 72 65 64 65 6e .json and creden
91c0: 74 69 61 6c 73 22 0a 6d 6b 64 69 72 20 2d 70 20 tials".mkdir -p
91d0: 24 44 2f 64 61 65 6d 6f 6e 2f 6e 6f 64 65 2f 63 $D/daemon/node/c
91e0: 72 65 64 65 6e 74 69 61 6c 73 20 24 44 2f 64 61 redentials $D/da
91f0: 65 6d 6f 6e 2f 6e 6f 64 65 2f 63 6c 6f 6e 65 73 emon/node/clones
9200: 3b 20 63 68 6d 6f 64 20 37 30 30 20 24 44 2f 64 ; chmod 700 $D/d
9210: 61 65 6d 6f 6e 2f 6e 6f 64 65 0a 70 72 69 6e 74 aemon/node.print
9220: 66 20 27 7b 22 75 73 65 72 22 3a 22 64 65 6d 6f f '{"user":"demo
9230: 2d 6e 6f 64 65 22 2c 22 70 61 73 73 77 6f 72 64 -node","password
9240: 22 3a 22 6e 6f 64 65 2d 70 77 22 2c 22 72 75 6e ":"node-pw","run
9250: 73 22 3a 7b 22 62 75 69 6c 64 65 72 22 3a 22 62 s":{"builder":"b
9260: 75 69 6c 64 65 72 2d 70 77 22 7d 7d 27 20 3e 20 uilder-pw"}}' >
9270: 24 44 2f 64 61 65 6d 6f 6e 2f 6e 6f 64 65 2f 63 $D/daemon/node/c
9280: 72 65 64 65 6e 74 69 61 6c 73 2f 64 65 6d 6f 2e redentials/demo.
9290: 6a 73 6f 6e 3b 20 63 68 6d 6f 64 20 36 30 30 20 json; chmod 600
92a0: 24 44 2f 64 61 65 6d 6f 6e 2f 6e 6f 64 65 2f 63 $D/daemon/node/c
92b0: 72 65 64 65 6e 74 69 61 6c 73 2f 64 65 6d 6f 2e redentials/demo.
92c0: 6a 73 6f 6e 0a 70 72 69 6e 74 66 20 27 5b 7b 22 json.printf '[{"
92d0: 63 6c 6f 6e 65 22 3a 22 25 73 22 2c 22 73 65 72 clone":"%s","ser
92e0: 76 65 72 22 3a 22 25 73 22 2c 22 63 72 65 64 65 ver":"%s","crede
92f0: 6e 74 69 61 6c 73 22 3a 22 25 73 22 7d 5d 27 20 ntials":"%s"}]'
9300: 24 44 2f 64 61 65 6d 6f 6e 2f 6e 6f 64 65 2f 63 $D/daemon/node/c
9310: 6c 6f 6e 65 73 2f 64 65 6d 6f 2e 66 6f 73 73 69 lones/demo.fossi
9320: 6c 20 24 55 20 24 44 2f 64 61 65 6d 6f 6e 2f 6e l $U $D/daemon/n
9330: 6f 64 65 2f 63 72 65 64 65 6e 74 69 61 6c 73 2f ode/credentials/
9340: 64 65 6d 6f 2e 6a 73 6f 6e 20 3e 20 24 44 2f 64 demo.json > $D/d
9350: 61 65 6d 6f 6e 2f 6e 6f 64 65 2f 6e 6f 64 65 73 aemon/node/nodes
9360: 2e 6a 73 6f 6e 3b 20 63 68 6d 6f 64 20 36 30 30 .json; chmod 600
9370: 20 24 44 2f 64 61 65 6d 6f 6e 2f 6e 6f 64 65 2f $D/daemon/node/
9380: 6e 6f 64 65 73 2e 6a 73 6f 6e 0a 0a 73 61 79 20 nodes.json..say
9390: 22 63 6f 6e 66 69 6e 65 64 20 64 61 65 6d 6f 6e "confined daemon
93a0: 2c 20 74 68 72 6f 75 67 68 20 74 68 65 20 6c 61 , through the la
93b0: 75 6e 63 68 65 72 27 73 20 64 61 65 6d 6f 6e 20 uncher's daemon
93c0: 70 6f 6c 69 63 79 22 0a 74 69 6d 65 6f 75 74 20 policy".timeout
93d0: 39 30 30 20 24 42 49 4e 2f 73 74 61 72 74 2d 6f 900 $BIN/start-o
93e0: 63 78 20 2d 2d 73 63 72 61 74 63 68 20 24 44 20 cx --scratch $D
93f0: 64 61 65 6d 6f 6e 20 3e 20 24 4f 55 54 2f 64 61 daemon > $OUT/da
9400: 65 6d 6f 6e 2e 6c 6f 67 20 32 3e 26 31 20 26 20 emon.log 2>&1 &
9410: 70 69 64 73 2b 3d 28 24 21 29 0a 66 6f 72 20 69 pids+=($!).for i
9420: 20 69 6e 20 24 28 73 65 71 20 36 30 29 3b 20 64 in $(seq 60); d
9430: 6f 20 5b 20 2d 65 20 24 44 2f 64 61 65 6d 6f 6e o [ -e $D/daemon
9440: 2f 6e 6f 64 65 2f 63 6c 6f 6e 65 73 2f 64 65 6d /node/clones/dem
9450: 6f 2e 66 6f 73 73 69 6c 20 5d 20 26 26 20 67 72 o.fossil ] && gr
9460: 65 70 20 2d 71 20 72 6f 75 6e 64 20 24 44 2f 64 ep -q round $D/d
9470: 61 65 6d 6f 6e 2f 6e 6f 64 65 2f 64 61 65 6d 6f aemon/node/daemo
9480: 6e 2e 6c 6f 67 20 32 3e 2f 64 65 76 2f 6e 75 6c n.log 2>/dev/nul
9490: 6c 20 26 26 20 62 72 65 61 6b 3b 20 73 6c 65 65 l && break; slee
94a0: 70 20 31 3b 20 64 6f 6e 65 0a 74 61 69 6c 20 2d p 1; done.tail -
94b0: 33 20 24 44 2f 64 61 65 6d 6f 6e 2f 6e 6f 64 65 3 $D/daemon/node
94c0: 2f 64 61 65 6d 6f 6e 2e 6c 6f 67 20 32 3e 2f 64 /daemon.log 2>/d
94d0: 65 76 2f 6e 75 6c 6c 3b 20 74 61 69 6c 20 2d 35 ev/null; tail -5
94e0: 20 24 4f 55 54 2f 64 61 65 6d 6f 6e 2e 6c 6f 67 $OUT/daemon.log
94f0: 0a 0a 23 20 41 20 70 72 6f 62 65 20 61 20 72 75 ..# A probe a ru
9500: 6e 27 73 20 70 72 6f 63 65 73 73 20 72 75 6e 73 n's process runs
9510: 3a 20 77 68 61 74 20 69 74 20 63 61 6e 20 73 65 : what it can se
9520: 65 20 6f 66 20 74 68 65 20 73 65 72 76 65 64 20 e of the served
9530: 72 65 70 6f 73 69 74 6f 72 69 65 73 2e 0a 50 52 repositories..PR
9540: 4f 42 45 3d 22 65 63 68 6f 20 75 69 64 3d 5c 24 OBE="echo uid=\$
9550: 28 69 64 20 2d 75 29 3b 20 66 6f 72 20 70 20 69 (id -u); for p i
9560: 6e 20 24 44 2f 66 6f 73 73 69 6c 20 24 52 3b 20 n $D/fossil $R;
9570: 64 6f 20 5b 20 2d 65 20 5c 24 70 20 5d 20 26 26 do [ -e \$p ] &&
9580: 20 65 63 68 6f 20 56 49 53 49 42 4c 45 20 5c 24 echo VISIBLE \$
9590: 70 20 7c 7c 20 65 63 68 6f 20 61 62 73 65 6e 74 p || echo absent
95a0: 20 5c 24 70 3b 20 64 6f 6e 65 3b 20 68 65 61 64 \$p; done; head
95b0: 20 2d 63 20 31 36 20 24 52 20 3e 2f 64 65 76 2f -c 16 $R >/dev/
95c0: 6e 75 6c 6c 20 32 3e 26 31 20 26 26 20 65 63 68 null 2>&1 && ech
95d0: 6f 20 52 45 41 44 2d 4f 4b 20 7c 7c 20 65 63 68 o READ-OK || ech
95e0: 6f 20 72 65 61 64 2d 72 65 66 75 73 65 64 3b 20 o read-refused;
95f0: 6e 3d 30 3b 20 66 6f 72 20 78 20 69 6e 20 2f 70 n=0; for x in /p
9600: 72 6f 63 2f 5b 30 2d 39 5d 2a 2f 72 6f 6f 74 24 roc/[0-9]*/root$
9610: 52 3b 20 64 6f 20 5b 20 2d 72 20 5c 24 78 20 5d R; do [ -r \$x ]
9620: 20 26 26 20 6e 3d 5c 24 28 28 6e 2b 31 29 29 3b && n=\$((n+1));
9630: 20 64 6f 6e 65 3b 20 65 63 68 6f 20 70 72 6f 63 done; echo proc
9640: 2d 70 61 74 68 73 2d 72 65 61 64 61 62 6c 65 3d -paths-readable=
9650: 5c 24 6e 22 0a 70 6f 73 74 5f 72 75 6e 28 29 20 \$n".post_run()
9660: 7b 20 23 20 24 31 20 74 69 74 6c 65 2c 20 24 32 { # $1 title, $2
9670: 20 72 75 6e 20 4a 53 4f 4e 3a 20 70 6f 73 74 65 run JSON: poste
9680: 64 20 62 79 20 62 6f 73 73 20 74 68 72 6f 75 67 d by boss throug
9690: 68 20 46 6f 73 73 69 6c 27 73 20 6f 77 6e 20 66 h Fossil's own f
96a0: 6f 72 6d 0a 20 20 70 79 74 68 6f 6e 33 20 2d 20 orm. python3 -
96b0: 22 24 55 22 20 22 24 31 22 20 22 24 32 22 20 3c "$U" "$1" "$2" <
96c0: 3c 27 45 27 0a 69 6d 70 6f 72 74 20 68 74 74 70 <'E'.import http
96d0: 2e 63 6f 6f 6b 69 65 6a 61 72 2c 20 72 65 2c 20 .cookiejar, re,
96e0: 73 79 73 2c 20 75 72 6c 6c 69 62 2e 70 61 72 73 sys, urllib.pars
96f0: 65 2c 20 75 72 6c 6c 69 62 2e 72 65 71 75 65 73 e, urllib.reques
9700: 74 0a 62 61 73 65 2c 20 74 69 74 6c 65 2c 20 72 t.base, title, r
9710: 75 6e 20 3d 20 73 79 73 2e 61 72 67 76 5b 31 3a un = sys.argv[1:
9720: 34 5d 0a 6a 61 72 20 3d 20 68 74 74 70 2e 63 6f 4].jar = http.co
9730: 6f 6b 69 65 6a 61 72 2e 43 6f 6f 6b 69 65 4a 61 okiejar.CookieJa
9740: 72 28 29 3b 20 6f 70 20 3d 20 75 72 6c 6c 69 62 r(); op = urllib
9750: 2e 72 65 71 75 65 73 74 2e 62 75 69 6c 64 5f 6f .request.build_o
9760: 70 65 6e 65 72 28 75 72 6c 6c 69 62 2e 72 65 71 pener(urllib.req
9770: 75 65 73 74 2e 48 54 54 50 43 6f 6f 6b 69 65 50 uest.HTTPCookieP
9780: 72 6f 63 65 73 73 6f 72 28 6a 61 72 29 29 0a 6f rocessor(jar)).o
9790: 70 2e 6f 70 65 6e 28 62 61 73 65 20 2b 20 22 2f p.open(base + "/
97a0: 6c 6f 67 69 6e 22 2c 20 75 72 6c 6c 69 62 2e 70 login", urllib.p
97b0: 61 72 73 65 2e 75 72 6c 65 6e 63 6f 64 65 28 7b arse.urlencode({
97c0: 22 75 22 3a 20 22 62 6f 73 73 22 2c 20 22 70 22 "u": "boss", "p"
97d0: 3a 20 22 62 6f 73 73 2d 70 77 22 2c 20 22 69 6e : "boss-pw", "in
97e0: 22 3a 20 22 31 22 7d 29 2e 65 6e 63 6f 64 65 28 ": "1"}).encode(
97f0: 29 29 0a 63 73 72 66 20 3d 20 72 65 2e 73 65 61 )).csrf = re.sea
9800: 72 63 68 28 72 27 6e 61 6d 65 3d 22 63 73 72 66 rch(r'name="csrf
9810: 22 20 76 61 6c 75 65 3d 22 28 5b 5e 22 5d 2b 29 " value="([^"]+)
9820: 22 27 2c 20 6f 70 2e 6f 70 65 6e 28 62 61 73 65 "', op.open(base
9830: 20 2b 20 22 2f 66 6f 72 75 6d 6e 65 77 22 29 2e + "/forumnew").
9840: 72 65 61 64 28 29 2e 64 65 63 6f 64 65 28 29 29 read().decode())
9850: 2e 67 72 6f 75 70 28 31 29 0a 72 65 71 20 3d 20 .group(1).req =
9860: 75 72 6c 6c 69 62 2e 72 65 71 75 65 73 74 2e 52 urllib.request.R
9870: 65 71 75 65 73 74 28 62 61 73 65 20 2b 20 22 2f equest(base + "/
9880: 66 6f 72 75 6d 65 31 22 2c 20 75 72 6c 6c 69 62 forume1", urllib
9890: 2e 70 61 72 73 65 2e 75 72 6c 65 6e 63 6f 64 65 .parse.urlencode
98a0: 28 7b 22 63 73 72 66 22 3a 20 63 73 72 66 2c 20 ({"csrf": csrf,
98b0: 22 74 69 74 6c 65 22 3a 20 74 69 74 6c 65 2c 20 "title": title,
98c0: 22 6d 69 6d 65 74 79 70 65 22 3a 20 22 74 65 78 "mimetype": "tex
98d0: 74 2f 78 2d 6d 61 72 6b 64 6f 77 6e 22 2c 20 22 t/x-markdown", "
98e0: 63 6f 6e 74 65 6e 74 22 3a 20 22 60 60 60 6a 73 content": "```js
98f0: 6f 6e 5c 6e 22 20 2b 20 72 75 6e 20 2b 20 22 5c on\n" + run + "\
9900: 6e 60 60 60 5c 6e 22 2c 20 22 73 75 62 6d 69 74 n```\n", "submit
9910: 22 3a 20 22 53 75 62 6d 69 74 22 7d 29 2e 65 6e ": "Submit"}).en
9920: 63 6f 64 65 28 29 29 0a 72 65 71 2e 61 64 64 5f code()).req.add_
9930: 68 65 61 64 65 72 28 22 52 65 66 65 72 65 72 22 header("Referer"
9940: 2c 20 62 61 73 65 20 2b 20 22 2f 66 6f 72 75 6d , base + "/forum
9950: 6e 65 77 22 29 0a 70 72 69 6e 74 28 6f 70 2e 6f new").print(op.o
9960: 70 65 6e 28 72 65 71 29 2e 67 65 74 75 72 6c 28 pen(req).geturl(
9970: 29 2e 72 73 70 6c 69 74 28 22 2f 22 2c 20 31 29 ).rsplit("/", 1)
9980: 5b 2d 31 5d 29 0a 45 0a 7d 0a 4a 50 52 4f 42 45 [-1]).E.}.JPROBE
9990: 3d 24 28 70 79 74 68 6f 6e 33 20 2d 63 20 27 69 =$(python3 -c 'i
99a0: 6d 70 6f 72 74 20 6a 73 6f 6e 2c 73 79 73 3b 20 mport json,sys;
99b0: 70 72 69 6e 74 28 6a 73 6f 6e 2e 64 75 6d 70 73 print(json.dumps
99c0: 28 73 79 73 2e 61 72 67 76 5b 31 5d 29 29 27 20 (sys.argv[1]))'
99d0: 22 24 50 52 4f 42 45 22 29 0a 53 41 4e 44 42 4f "$PROBE").SANDBO
99e0: 58 45 44 3d 27 7b 22 6f 63 78 22 3a 20 31 2c 20 XED='{"ocx": 1,
99f0: 22 6b 69 6e 64 22 3a 20 22 72 75 6e 22 2c 20 22 "kind": "run", "
9a00: 6e 6f 64 65 22 3a 20 22 64 65 6d 6f 2d 6e 6f 64 node": "demo-nod
9a10: 65 22 2c 20 22 6f 62 6a 65 63 74 69 76 65 22 3a e", "objective":
9a20: 20 22 50 72 6f 62 65 20 66 72 6f 6d 20 61 6e 20 "Probe from an
9a30: 61 67 65 6e 74 20 73 61 6e 64 62 6f 78 2e 22 2c agent sandbox.",
9a40: 20 22 77 6f 72 6b 73 70 61 63 65 22 3a 20 5b 7b "workspace": [{
9a50: 22 73 74 65 70 22 3a 20 22 64 69 72 65 63 74 6f "step": "directo
9a60: 72 79 22 2c 20 22 61 74 22 3a 20 22 24 24 72 6f ry", "at": "$$ro
9a70: 6f 74 24 24 2f 24 24 75 73 65 72 24 24 2f 70 72 ot$$/$$user$$/pr
9a80: 6f 6a 65 63 74 73 2f 24 24 70 72 6f 6a 65 63 74 ojects/$$project
9a90: 24 24 2f 24 24 72 75 6e 24 24 22 7d 5d 2c 20 22 $$/$$run$$"}], "
9aa0: 73 61 6e 64 62 6f 78 22 3a 20 7b 22 75 69 64 22 sandbox": {"uid"
9ab0: 3a 20 31 30 30 30 2c 20 22 67 69 64 22 3a 20 31 : 1000, "gid": 1
9ac0: 30 30 30 2c 20 22 63 61 70 61 62 69 6c 69 74 69 000, "capabiliti
9ad0: 65 73 22 3a 20 5b 5d 2c 20 22 6e 65 74 77 6f 72 es": [], "networ
9ae0: 6b 22 3a 20 66 61 6c 73 65 2c 20 22 6f 77 6e 5f k": false, "own_
9af0: 73 65 73 73 69 6f 6e 22 3a 20 74 72 75 65 2c 20 session": true,
9b00: 22 65 6e 64 73 5f 77 69 74 68 5f 6e 6f 64 65 22 "ends_with_node"
9b10: 3a 20 74 72 75 65 2c 20 22 72 65 61 64 5f 6f 6e : true, "read_on
9b20: 6c 79 22 3a 20 5b 22 2f 75 73 72 22 2c 20 22 2f ly": ["/usr", "/
9b30: 62 69 6e 22 2c 20 22 2f 6c 69 62 22 2c 20 22 2f bin", "/lib", "/
9b40: 6c 69 62 36 34 22 5d 2c 20 22 72 65 61 64 5f 77 lib64"], "read_w
9b50: 72 69 74 65 22 3a 20 5b 22 24 24 77 6f 72 6b 73 rite": ["$$works
9b60: 70 61 63 65 24 24 22 5d 2c 20 22 68 6f 6d 65 73 pace$$"], "homes
9b70: 22 3a 20 5b 5d 2c 20 22 66 69 6c 65 73 22 3a 20 ": [], "files":
9b80: 5b 5d 2c 20 22 70 72 6f 63 22 3a 20 22 2f 70 72 [], "proc": "/pr
9b90: 6f 63 22 2c 20 22 64 65 76 22 3a 20 22 2f 64 65 oc", "dev": "/de
9ba0: 76 22 2c 20 22 74 6d 70 22 3a 20 22 2f 74 6d 70 v", "tmp": "/tmp
9bb0: 22 7d 2c 20 22 6c 69 6d 69 74 73 22 3a 20 7b 22 "}, "limits": {"
9bc0: 77 61 6c 6c 22 3a 20 22 32 6d 22 7d 2c 20 22 70 wall": "2m"}, "p
9bd0: 72 6f 63 65 73 73 22 3a 20 7b 22 75 73 65 72 22 rocess": {"user"
9be0: 3a 20 22 62 75 69 6c 64 65 72 22 2c 20 22 63 6f : "builder", "co
9bf0: 6d 6d 61 6e 64 22 3a 20 5b 22 2f 62 69 6e 2f 73 mmand": ["/bin/s
9c00: 68 22 2c 20 22 2d 63 22 2c 20 27 22 24 4a 50 52 h", "-c", '"$JPR
9c10: 4f 42 45 22 27 5d 2c 20 22 65 6e 76 22 3a 20 7b OBE"'], "env": {
9c20: 22 50 41 54 48 22 3a 20 22 2f 75 73 72 2f 62 69 "PATH": "/usr/bi
9c30: 6e 3a 2f 62 69 6e 22 7d 2c 20 22 6b 65 65 70 22 n:/bin"}, "keep"
9c40: 3a 20 7b 22 73 74 64 6f 75 74 5f 77 68 65 6e 22 : {"stdout_when"
9c50: 3a 20 22 61 6c 77 61 79 73 22 7d 7d 7d 27 0a 42 : "always"}}}'.B
9c60: 41 52 45 3d 27 7b 22 6f 63 78 22 3a 20 31 2c 20 ARE='{"ocx": 1,
9c70: 22 6b 69 6e 64 22 3a 20 22 72 75 6e 22 2c 20 22 "kind": "run", "
9c80: 6e 6f 64 65 22 3a 20 22 64 65 6d 6f 2d 6e 6f 64 node": "demo-nod
9c90: 65 22 2c 20 22 6f 62 6a 65 63 74 69 76 65 22 3a e", "objective":
9ca0: 20 22 50 72 6f 62 65 20 66 72 6f 6d 20 74 68 65 "Probe from the
9cb0: 20 64 61 65 6d 6f 6e 20 69 74 73 65 6c 66 2c 20 daemon itself,
9cc0: 75 6e 63 6f 6e 66 69 6e 65 64 20 62 79 20 61 6e unconfined by an
9cd0: 79 20 72 75 6e 20 73 61 6e 64 62 6f 78 2e 22 2c y run sandbox.",
9ce0: 20 22 70 72 6f 63 65 73 73 22 3a 20 7b 22 75 73 "process": {"us
9cf0: 65 72 22 3a 20 22 62 75 69 6c 64 65 72 22 2c 20 er": "builder",
9d00: 22 63 6f 6d 6d 61 6e 64 22 3a 20 5b 22 2f 62 69 "command": ["/bi
9d10: 6e 2f 73 68 22 2c 20 22 2d 63 22 2c 20 27 22 24 n/sh", "-c", '"$
9d20: 4a 50 52 4f 42 45 22 27 5d 2c 20 22 65 6e 76 22 JPROBE"'], "env"
9d30: 3a 20 7b 22 50 41 54 48 22 3a 20 22 2f 75 73 72 : {"PATH": "/usr
9d40: 2f 62 69 6e 3a 2f 62 69 6e 22 7d 2c 20 22 6b 65 /bin:/bin"}, "ke
9d50: 65 70 22 3a 20 7b 22 73 74 64 6f 75 74 5f 77 68 ep": {"stdout_wh
9d60: 65 6e 22 3a 20 22 61 6c 77 61 79 73 22 7d 7d 7d en": "always"}}}
9d70: 27 0a 0a 73 61 79 20 22 74 77 6f 20 72 75 6e 73 '..say "two runs
9d80: 2c 20 70 6f 73 74 65 64 20 62 79 20 62 6f 73 73 , posted by boss
9d90: 20 28 73 65 74 75 70 29 20 74 68 72 6f 75 67 68 (setup) through
9da0: 20 46 6f 73 73 69 6c 27 73 20 66 6f 72 6d 22 0a Fossil's form".
9db0: 52 55 4e 31 3d 24 28 70 6f 73 74 5f 72 75 6e 20 RUN1=$(post_run
9dc0: 22 73 61 6e 64 62 6f 78 65 64 20 70 72 6f 62 65 "sandboxed probe
9dd0: 22 20 22 24 53 41 4e 44 42 4f 58 45 44 22 29 3b " "$SANDBOXED");
9de0: 20 65 63 68 6f 20 22 73 61 6e 64 62 6f 78 65 64 echo "sandboxed
9df0: 20 72 75 6e 20 24 52 55 4e 31 22 0a 52 55 4e 32 run $RUN1".RUN2
9e00: 3d 24 28 70 6f 73 74 5f 72 75 6e 20 22 75 6e 63 =$(post_run "unc
9e10: 6f 6e 66 69 6e 65 64 20 70 72 6f 62 65 22 20 22 onfined probe" "
9e20: 24 42 41 52 45 22 29 3b 20 65 63 68 6f 20 22 75 $BARE"); echo "u
9e30: 6e 63 6f 6e 66 69 6e 65 64 20 72 75 6e 20 24 52 nconfined run $R
9e40: 55 4e 32 22 0a 66 6f 72 20 69 20 69 6e 20 24 28 UN2".for i in $(
9e50: 73 65 71 20 39 30 29 3b 20 64 6f 20 5b 20 22 24 seq 90); do [ "$
9e60: 28 24 46 20 73 71 6c 20 2d 52 20 24 52 20 22 53 ($F sql -R $R "S
9e70: 45 4c 45 43 54 20 63 6f 75 6e 74 28 2a 29 20 46 ELECT count(*) F
9e80: 52 4f 4d 20 66 6f 72 75 6d 70 6f 73 74 20 66 20 ROM forumpost f
9e90: 4a 4f 49 4e 20 62 6c 6f 62 20 62 20 4f 4e 20 62 JOIN blob b ON b
9ea0: 2e 72 69 64 3d 66 2e 66 70 69 64 20 57 48 45 52 .rid=f.fpid WHER
9eb0: 45 20 62 2e 63 6f 6e 74 65 6e 74 20 49 53 20 4e E b.content IS N
9ec0: 4f 54 20 4e 55 4c 4c 22 20 32 3e 2f 64 65 76 2f OT NULL" 2>/dev/
9ed0: 6e 75 6c 6c 29 22 20 2d 67 65 20 36 20 5d 20 26 null)" -ge 6 ] &
9ee0: 26 20 62 72 65 61 6b 3b 20 73 6c 65 65 70 20 31 & break; sleep 1
9ef0: 3b 20 64 6f 6e 65 0a 73 6c 65 65 70 20 33 0a 73 ; done.sleep 3.s
9f00: 61 79 20 22 77 68 61 74 20 74 68 65 20 72 75 6e ay "what the run
9f10: 73 20 70 6f 73 74 65 64 20 28 73 74 61 72 74 65 s posted (starte
9f20: 64 2c 20 6b 65 70 74 20 6c 6f 67 73 2c 20 65 6e d, kept logs, en
9f30: 64 65 64 29 22 0a 24 46 20 73 71 6c 20 2d 52 20 ded)".$F sql -R
9f40: 24 52 20 22 53 45 4c 45 43 54 20 73 75 62 73 74 $R "SELECT subst
9f50: 72 28 62 2e 75 75 69 64 2c 31 2c 31 30 29 2c 20 r(b.uuid,1,10),
9f60: 75 2e 6c 6f 67 69 6e 2c 20 73 75 62 73 74 72 28 u.login, substr(
9f70: 72 65 70 6c 61 63 65 28 72 65 70 6c 61 63 65 28 replace(replace(
9f80: 43 41 53 54 28 63 6f 6e 74 65 6e 74 28 62 2e 75 CAST(content(b.u
9f90: 75 69 64 29 20 41 53 20 54 45 58 54 29 2c 63 68 uid) AS TEXT),ch
9fa0: 61 72 28 31 30 29 2c 27 20 27 29 2c 27 20 20 27 ar(10),' '),' '
9fb0: 2c 27 20 27 29 2c 31 2c 36 30 30 29 20 46 52 4f ,' '),1,600) FRO
9fc0: 4d 20 66 6f 72 75 6d 70 6f 73 74 20 66 20 4a 4f M forumpost f JO
9fd0: 49 4e 20 62 6c 6f 62 20 62 20 4f 4e 20 62 2e 72 IN blob b ON b.r
9fe0: 69 64 3d 66 2e 66 70 69 64 20 4c 45 46 54 20 4a id=f.fpid LEFT J
9ff0: 4f 49 4e 20 72 63 76 66 72 6f 6d 20 72 20 4f 4e OIN rcvfrom r ON
a000: 20 72 2e 72 63 76 69 64 3d 62 2e 72 63 76 69 64 r.rcvid=b.rcvid
a010: 20 4c 45 46 54 20 4a 4f 49 4e 20 75 73 65 72 20 LEFT JOIN user
a020: 75 20 4f 4e 20 75 2e 75 69 64 3d 72 2e 75 69 64 u ON u.uid=r.uid
a030: 20 4f 52 44 45 52 20 42 59 20 66 2e 66 6d 74 69 ORDER BY f.fmti
a040: 6d 65 22 20 7c 20 74 61 69 6c 20 2d 6e 20 2b 31 me" | tail -n +1
a050: 0a 0a 73 61 79 20 22 72 65 63 65 69 70 74 73 20 ..say "receipts
a060: 74 68 72 6f 75 67 68 20 74 68 65 20 6c 61 75 6e through the laun
a070: 63 68 65 72 27 73 20 43 47 49 20 72 6f 75 74 65 cher's CGI route
a080: 2c 20 61 67 61 69 6e 73 74 20 46 6f 73 73 69 6c , against Fossil
a090: 27 73 20 6f 77 6e 20 72 65 63 6f 72 64 22 0a 48 's own record".H
a0a0: 31 3d 24 28 24 46 20 73 71 6c 20 2d 52 20 24 52 1=$($F sql -R $R
a0b0: 20 22 53 45 4c 45 43 54 20 62 2e 75 75 69 64 20 "SELECT b.uuid
a0c0: 46 52 4f 4d 20 62 6c 6f 62 20 62 20 4a 4f 49 4e FROM blob b JOIN
a0d0: 20 66 6f 72 75 6d 70 6f 73 74 20 66 20 4f 4e 20 forumpost f ON
a0e0: 66 2e 66 70 69 64 3d 62 2e 72 69 64 20 4f 52 44 f.fpid=b.rid ORD
a0f0: 45 52 20 42 59 20 66 2e 66 6d 74 69 6d 65 20 4c ER BY f.fmtime L
a100: 49 4d 49 54 20 31 22 20 7c 20 74 72 20 2d 64 20 IMIT 1" | tr -d
a110: 22 27 22 29 0a 66 6f 72 20 77 68 6f 20 69 6e 20 "'").for who in
a120: 22 62 6f 73 73 20 62 6f 73 73 2d 70 77 22 20 22 "boss boss-pw" "
a130: 76 69 65 77 65 72 20 76 69 65 77 65 72 2d 70 77 viewer viewer-pw
a140: 22 20 22 61 6e 6f 6e 20 2d 22 3b 20 64 6f 20 73 " "anon -"; do s
a150: 65 74 20 2d 2d 20 24 77 68 6f 0a 20 20 70 79 74 et -- $who. pyt
a160: 68 6f 6e 33 20 2d 20 22 24 55 22 20 22 24 31 22 hon3 - "$U" "$1"
a170: 20 22 24 32 22 20 22 24 48 31 22 20 3c 3c 27 45 "$2" "$H1" <<'E
a180: 27 0a 69 6d 70 6f 72 74 20 68 74 74 70 2e 63 6f '.import http.co
a190: 6f 6b 69 65 6a 61 72 2c 20 6a 73 6f 6e 2c 20 73 okiejar, json, s
a1a0: 79 73 2c 20 75 72 6c 6c 69 62 2e 70 61 72 73 65 ys, urllib.parse
a1b0: 2c 20 75 72 6c 6c 69 62 2e 72 65 71 75 65 73 74 , urllib.request
a1c0: 0a 62 61 73 65 2c 20 75 73 65 72 2c 20 70 77 2c .base, user, pw,
a1d0: 20 68 20 3d 20 73 79 73 2e 61 72 67 76 5b 31 3a h = sys.argv[1:
a1e0: 35 5d 0a 6a 61 72 20 3d 20 68 74 74 70 2e 63 6f 5].jar = http.co
a1f0: 6f 6b 69 65 6a 61 72 2e 43 6f 6f 6b 69 65 4a 61 okiejar.CookieJa
a200: 72 28 29 3b 20 6f 70 20 3d 20 75 72 6c 6c 69 62 r(); op = urllib
a210: 2e 72 65 71 75 65 73 74 2e 62 75 69 6c 64 5f 6f .request.build_o
a220: 70 65 6e 65 72 28 75 72 6c 6c 69 62 2e 72 65 71 pener(urllib.req
a230: 75 65 73 74 2e 48 54 54 50 43 6f 6f 6b 69 65 50 uest.HTTPCookieP
a240: 72 6f 63 65 73 73 6f 72 28 6a 61 72 29 29 0a 69 rocessor(jar)).i
a250: 66 20 70 77 20 21 3d 20 22 2d 22 3a 20 6f 70 2e f pw != "-": op.
a260: 6f 70 65 6e 28 62 61 73 65 20 2b 20 22 2f 6c 6f open(base + "/lo
a270: 67 69 6e 22 2c 20 75 72 6c 6c 69 62 2e 70 61 72 gin", urllib.par
a280: 73 65 2e 75 72 6c 65 6e 63 6f 64 65 28 7b 22 75 se.urlencode({"u
a290: 22 3a 20 75 73 65 72 2c 20 22 70 22 3a 20 70 77 ": user, "p": pw
a2a0: 2c 20 22 69 6e 22 3a 20 22 31 22 7d 29 2e 65 6e , "in": "1"}).en
a2b0: 63 6f 64 65 28 29 29 0a 74 72 79 3a 0a 20 20 20 code()).try:.
a2c0: 20 72 20 3d 20 6f 70 2e 6f 70 65 6e 28 62 61 73 r = op.open(bas
a2d0: 65 20 2b 20 22 2f 65 78 74 2f 6f 63 78 32 2d 63 e + "/ext/ocx2-c
a2e0: 67 69 2f 72 65 63 65 69 70 74 22 2c 20 75 72 6c gi/receipt", url
a2f0: 6c 69 62 2e 70 61 72 73 65 2e 75 72 6c 65 6e 63 lib.parse.urlenc
a300: 6f 64 65 28 7b 22 6a 73 6f 6e 22 3a 20 22 6f 6e ode({"json": "on
a310: 22 2c 20 22 61 72 74 69 66 61 63 74 73 22 3a 20 ", "artifacts":
a320: 68 2c 20 22 63 61 70 61 62 69 6c 69 74 69 65 73 h, "capabilities
a330: 5f 6f 66 22 3a 20 22 64 65 6d 6f 2d 6e 6f 64 65 _of": "demo-node
a340: 22 7d 29 2e 65 6e 63 6f 64 65 28 29 29 0a 20 20 "}).encode()).
a350: 20 20 61 20 3d 20 6a 73 6f 6e 2e 6c 6f 61 64 28 a = json.load(
a360: 72 29 0a 65 78 63 65 70 74 20 75 72 6c 6c 69 62 r).except urllib
a370: 2e 65 72 72 6f 72 2e 48 54 54 50 45 72 72 6f 72 .error.HTTPError
a380: 20 61 73 20 65 3a 0a 20 20 20 20 61 20 3d 20 7b as e:. a = {
a390: 22 68 74 74 70 22 3a 20 65 2e 63 6f 64 65 2c 20 "http": e.code,
a3a0: 22 62 6f 64 79 22 3a 20 65 2e 72 65 61 64 28 29 "body": e.read()
a3b0: 2e 64 65 63 6f 64 65 28 29 5b 3a 33 30 30 5d 7d .decode()[:300]}
a3c0: 0a 70 72 69 6e 74 28 75 73 65 72 2c 20 6a 73 6f .print(user, jso
a3d0: 6e 2e 64 75 6d 70 73 28 61 2e 67 65 74 28 22 72 n.dumps(a.get("r
a3e0: 65 73 75 6c 74 22 2c 20 61 29 29 29 0a 45 0a 64 esult", a))).E.d
a3f0: 6f 6e 65 0a 65 63 68 6f 20 22 46 6f 73 73 69 6c one.echo "Fossil
a400: 27 73 20 6f 77 6e 20 72 65 63 6f 72 64 3a 20 24 's own record: $
a410: 28 24 46 20 73 71 6c 20 2d 52 20 24 52 20 22 53 ($F sql -R $R "S
a420: 45 4c 45 43 54 20 75 2e 6c 6f 67 69 6e 2c 20 66 ELECT u.login, f
a430: 75 6c 6c 63 61 70 28 75 2e 63 61 70 29 2c 20 62 ullcap(u.cap), b
a440: 2e 72 63 76 69 64 2c 20 62 2e 72 69 64 20 46 52 .rcvid, b.rid FR
a450: 4f 4d 20 62 6c 6f 62 20 62 20 4a 4f 49 4e 20 72 OM blob b JOIN r
a460: 63 76 66 72 6f 6d 20 72 20 55 53 49 4e 47 28 72 cvfrom r USING(r
a470: 63 76 69 64 29 20 4a 4f 49 4e 20 75 73 65 72 20 cvid) JOIN user
a480: 75 20 55 53 49 4e 47 28 75 69 64 29 20 57 48 45 u USING(uid) WHE
a490: 52 45 20 62 2e 75 75 69 64 3d 27 24 48 31 27 22 RE b.uuid='$H1'"
a4a0: 29 22 0a 65 63 68 6f 20 22 46 6f 73 73 69 6c 27 )".echo "Fossil'
a4b0: 73 20 66 75 6c 6c 63 61 70 28 64 65 6d 6f 2d 6e s fullcap(demo-n
a4c0: 6f 64 65 29 3a 20 24 28 24 46 20 73 71 6c 20 2d ode): $($F sql -
a4d0: 52 20 24 52 20 22 53 45 4c 45 43 54 20 66 75 6c R $R "SELECT ful
a4e0: 6c 63 61 70 28 63 61 70 29 20 46 52 4f 4d 20 75 lcap(cap) FROM u
a4f0: 73 65 72 20 57 48 45 52 45 20 6c 6f 67 69 6e 3d ser WHERE login=
a500: 27 64 65 6d 6f 2d 6e 6f 64 65 27 22 29 22 0a 0a 'demo-node'")"..
a510: 73 61 79 20 22 74 68 65 20 63 6f 6e 73 6f 6c 65 say "the console
a520: 2c 20 61 73 20 62 6f 73 73 20 28 74 68 65 20 70 , as boss (the p
a530: 61 67 65 27 73 20 6f 74 68 65 72 20 70 61 74 68 age's other path
a540: 73 20 73 74 69 6c 6c 20 70 61 73 73 20 74 68 72 s still pass thr
a550: 6f 75 67 68 3a 20 45 36 29 22 0a 70 79 74 68 6f ough: E6)".pytho
a560: 6e 33 20 2d 20 22 24 55 22 20 3c 3c 27 45 27 0a n3 - "$U" <<'E'.
a570: 69 6d 70 6f 72 74 20 68 74 74 70 2e 63 6f 6f 6b import http.cook
a580: 69 65 6a 61 72 2c 20 73 79 73 2c 20 75 72 6c 6c iejar, sys, urll
a590: 69 62 2e 70 61 72 73 65 2c 20 75 72 6c 6c 69 62 ib.parse, urllib
a5a0: 2e 72 65 71 75 65 73 74 0a 62 61 73 65 20 3d 20 .request.base =
a5b0: 73 79 73 2e 61 72 67 76 5b 31 5d 0a 6a 61 72 20 sys.argv[1].jar
a5c0: 3d 20 68 74 74 70 2e 63 6f 6f 6b 69 65 6a 61 72 = http.cookiejar
a5d0: 2e 43 6f 6f 6b 69 65 4a 61 72 28 29 3b 20 6f 70 .CookieJar(); op
a5e0: 20 3d 20 75 72 6c 6c 69 62 2e 72 65 71 75 65 73 = urllib.reques
a5f0: 74 2e 62 75 69 6c 64 5f 6f 70 65 6e 65 72 28 75 t.build_opener(u
a600: 72 6c 6c 69 62 2e 72 65 71 75 65 73 74 2e 48 54 rllib.request.HT
a610: 54 50 43 6f 6f 6b 69 65 50 72 6f 63 65 73 73 6f TPCookieProcesso
a620: 72 28 6a 61 72 29 29 0a 6f 70 2e 6f 70 65 6e 28 r(jar)).op.open(
a630: 62 61 73 65 20 2b 20 22 2f 6c 6f 67 69 6e 22 2c base + "/login",
a640: 20 75 72 6c 6c 69 62 2e 70 61 72 73 65 2e 75 72 urllib.parse.ur
a650: 6c 65 6e 63 6f 64 65 28 7b 22 75 22 3a 20 22 62 lencode({"u": "b
a660: 6f 73 73 22 2c 20 22 70 22 3a 20 22 62 6f 73 73 oss", "p": "boss
a670: 2d 70 77 22 2c 20 22 69 6e 22 3a 20 22 31 22 7d -pw", "in": "1"}
a680: 29 2e 65 6e 63 6f 64 65 28 29 29 0a 72 20 3d 20 ).encode()).r =
a690: 6f 70 2e 6f 70 65 6e 28 62 61 73 65 20 2b 20 22 op.open(base + "
a6a0: 2f 65 78 74 2f 6f 63 78 32 2d 63 67 69 2f 63 6f /ext/ocx2-cgi/co
a6b0: 6e 73 6f 6c 65 3f 6a 73 6f 6e 3d 6f 6e 22 29 3b nsole?json=on");
a6c0: 20 69 6d 70 6f 72 74 20 6a 73 6f 6e 3b 20 61 20 import json; a
a6d0: 3d 20 6a 73 6f 6e 2e 6c 6f 61 64 28 72 29 0a 72 = json.load(r).r
a6e0: 65 73 20 3d 20 61 2e 67 65 74 28 22 72 65 73 75 es = a.get("resu
a6f0: 6c 74 22 29 20 6f 72 20 7b 7d 0a 70 72 69 6e 74 lt") or {}.print
a700: 28 22 63 6f 6e 73 6f 6c 65 20 6f 6b 3a 22 2c 20 ("console ok:",
a710: 61 2e 67 65 74 28 22 73 74 6f 70 70 65 64 22 29 a.get("stopped")
a720: 20 69 73 20 4e 6f 6e 65 2c 20 22 7c 20 72 75 6e is None, "| run
a730: 73 20 72 75 6e 6e 69 6e 67 3a 22 2c 20 6c 65 6e s running:", len
a740: 28 72 65 73 2e 67 65 74 28 22 72 75 6e 6e 69 6e (res.get("runnin
a750: 67 22 2c 20 5b 5d 29 29 2c 20 22 7c 20 65 6e 64 g", [])), "| end
a760: 65 64 3a 22 2c 20 6c 65 6e 28 72 65 73 2e 67 65 ed:", len(res.ge
a770: 74 28 22 65 6e 64 65 64 22 2c 20 5b 5d 29 29 29 t("ended", [])))
a780: 0a 45 0a 0a 73 61 79 20 22 77 72 69 74 65 73 20 .E..say "writes
a790: 66 72 6f 6d 20 74 68 65 20 72 65 63 65 69 70 74 from the receipt
a7a0: 20 73 65 72 76 69 63 65 27 73 20 70 6f 6c 69 63 service's polic
a7b0: 79 20 66 61 69 6c 20 28 61 20 70 72 6f 62 65 20 y fail (a probe
a7c0: 69 6e 20 70 6c 61 63 65 20 6f 66 20 72 65 63 65 in place of rece
a7d0: 69 70 74 73 29 22 0a 50 42 3d 24 4f 55 54 2f 70 ipts)".PB=$OUT/p
a7e0: 72 6f 62 65 3b 20 6d 6b 64 69 72 20 2d 70 20 24 robe; mkdir -p $
a7f0: 50 42 3b 20 63 70 20 24 42 49 4e 2f 73 74 61 72 PB; cp $BIN/star
a800: 74 2d 6f 63 78 20 24 50 42 2f 73 74 61 72 74 2d t-ocx $PB/start-
a810: 6f 63 78 0a 63 61 74 20 3e 20 24 4f 55 54 2f 70 ocx.cat > $OUT/p
a820: 72 6f 62 65 2e 63 72 20 3c 3c 27 45 27 0a 72 65 robe.cr <<'E'.re
a830: 71 75 69 72 65 20 22 73 6f 63 6b 65 74 22 0a 73 quire "socket".s
a840: 6f 63 6b 65 74 2c 20 64 69 72 20 3d 20 41 52 47 ocket, dir = ARG
a850: 56 0a 72 65 70 6f 20 3d 20 44 69 72 2e 63 68 69 V.repo = Dir.chi
a860: 6c 64 72 65 6e 28 64 69 72 29 2e 66 69 6e 64 28 ldren(dir).find(
a870: 26 2e 65 6e 64 73 5f 77 69 74 68 3f 28 22 2e 66 &.ends_with?(".f
a880: 6f 73 73 69 6c 22 29 29 2e 6e 6f 74 5f 6e 69 6c ossil")).not_nil
a890: 21 0a 70 61 74 68 20 3d 20 46 69 6c 65 2e 6a 6f !.path = File.jo
a8a0: 69 6e 28 64 69 72 2c 20 72 65 70 6f 29 0a 74 72 in(dir, repo).tr
a8b0: 69 65 73 20 3d 20 7b 0a 20 20 22 6f 70 65 6e 20 ies = {. "open
a8c0: 74 68 65 20 72 65 70 6f 73 69 74 6f 72 79 20 66 the repository f
a8d0: 6f 72 20 77 72 69 74 69 6e 67 22 20 3d 3e 20 2d or writing" => -
a8e0: 3e 20 7b 20 46 69 6c 65 2e 6f 70 65 6e 28 70 61 > { File.open(pa
a8f0: 74 68 2c 20 22 72 2b 22 29 20 7b 20 7d 3b 20 22 th, "r+") { }; "
a900: 4f 50 45 4e 45 44 20 28 62 61 64 29 22 20 7d 2c OPENED (bad)" },
a910: 0a 20 20 22 63 72 65 61 74 65 20 61 20 66 69 6c . "create a fil
a920: 65 20 62 65 73 69 64 65 20 69 74 22 20 20 20 20 e beside it"
a930: 20 20 20 20 20 3d 3e 20 2d 3e 20 7b 20 46 69 6c => -> { Fil
a940: 65 2e 77 72 69 74 65 28 46 69 6c 65 2e 6a 6f 69 e.write(File.joi
a950: 6e 28 64 69 72 2c 20 22 78 22 29 2c 20 22 78 22 n(dir, "x"), "x"
a960: 29 3b 20 22 43 52 45 41 54 45 44 20 28 62 61 64 ); "CREATED (bad
a970: 29 22 20 7d 2c 0a 20 20 22 72 65 61 64 20 74 68 )" },. "read th
a980: 65 20 72 65 70 6f 73 69 74 6f 72 79 22 20 20 20 e repository"
a990: 20 20 20 20 20 20 20 20 20 20 3d 3e 20 2d 3e 20 => ->
a9a0: 7b 20 46 69 6c 65 2e 6f 70 65 6e 28 70 61 74 68 { File.open(path
a9b0: 29 20 7b 20 7c 66 7c 20 66 2e 72 65 61 64 5f 62 ) { |f| f.read_b
a9c0: 79 74 65 20 7d 3b 20 22 72 65 61 64 20 6f 6b 20 yte }; "read ok
a9d0: 28 61 6c 6c 6f 77 65 64 3a 20 72 65 61 64 2d 6f (allowed: read-o
a9e0: 6e 6c 79 29 22 20 7d 2c 0a 20 20 22 72 65 61 63 nly)" },. "reac
a9f0: 68 20 46 6f 73 73 69 6c 27 73 20 70 6f 72 74 22 h Fossil's port"
aa00: 20 20 20 20 20 20 20 20 20 20 20 20 20 3d 3e 20 =>
aa10: 2d 3e 20 7b 20 54 43 50 53 6f 63 6b 65 74 2e 6e -> { TCPSocket.n
aa20: 65 77 28 22 31 32 37 2e 30 2e 30 2e 31 22 2c 20 ew("127.0.0.1",
aa30: 46 69 6c 65 2e 72 65 61 64 28 46 69 6c 65 2e 6a File.read(File.j
aa40: 6f 69 6e 28 46 69 6c 65 2e 64 69 72 6e 61 6d 65 oin(File.dirname
aa50: 28 73 6f 63 6b 65 74 29 2c 20 22 70 6f 72 74 22 (socket), "port"
aa60: 29 29 2e 73 74 72 69 70 2e 74 6f 5f 69 2c 20 63 )).strip.to_i, c
aa70: 6f 6e 6e 65 63 74 5f 74 69 6d 65 6f 75 74 3a 20 onnect_timeout:
aa80: 32 2e 73 65 63 6f 6e 64 73 29 2e 63 6c 6f 73 65 2.seconds).close
aa90: 3b 20 22 43 4f 4e 4e 45 43 54 45 44 20 28 62 61 ; "CONNECTED (ba
aaa0: 64 29 22 20 7d 2c 0a 20 20 22 77 72 69 74 65 20 d)" },. "write
aab0: 69 6e 20 69 74 73 20 73 6f 63 6b 65 74 20 64 69 in its socket di
aac0: 72 65 63 74 6f 72 79 22 20 20 20 3d 3e 20 2d 3e rectory" => ->
aad0: 20 7b 20 46 69 6c 65 2e 77 72 69 74 65 28 46 69 { File.write(Fi
aae0: 6c 65 2e 6a 6f 69 6e 28 46 69 6c 65 2e 64 69 72 le.join(File.dir
aaf0: 6e 61 6d 65 28 73 6f 63 6b 65 74 29 2c 20 22 70 name(socket), "p
ab00: 72 6f 62 65 22 29 2c 20 22 78 22 29 3b 20 22 77 robe"), "x"); "w
ab10: 72 69 74 74 65 6e 20 28 61 6c 6c 6f 77 65 64 3a ritten (allowed:
ab20: 20 69 74 73 20 6f 6e 65 20 77 72 69 74 61 62 6c its one writabl
ab30: 65 20 70 6c 61 63 65 29 22 20 7d 2c 0a 7d 0a 74 e place)" },.}.t
ab40: 72 69 65 73 2e 65 61 63 68 20 64 6f 20 7c 77 68 ries.each do |wh
ab50: 61 74 2c 20 74 72 79 7c 0a 20 20 6f 75 74 63 6f at, try|. outco
ab60: 6d 65 20 3d 20 62 65 67 69 6e 0a 20 20 20 20 74 me = begin. t
ab70: 72 79 2e 63 61 6c 6c 0a 20 20 72 65 73 63 75 65 ry.call. rescue
ab80: 20 65 0a 20 20 20 20 22 72 65 66 75 73 65 64 20 e. "refused
ab90: 28 23 7b 65 2e 63 6c 61 73 73 7d 3a 20 23 7b 65 (#{e.class}: #{e
aba0: 2e 6d 65 73 73 61 67 65 2e 74 72 79 20 26 2e 6c .message.try &.l
abb0: 69 6e 65 73 2e 66 69 72 73 74 7d 29 22 0a 20 20 ines.first})".
abc0: 65 6e 64 0a 20 20 70 75 74 73 20 22 23 7b 77 68 end. puts "#{wh
abd0: 61 74 7d 3a 20 23 7b 6f 75 74 63 6f 6d 65 7d 22 at}: #{outcome}"
abe0: 0a 65 6e 64 0a 45 0a 65 63 68 6f 20 24 50 20 3e .end.E.echo $P >
abf0: 20 24 44 2f 72 75 6e 2f 70 6f 72 74 3b 20 63 75 $D/run/port; cu
ac00: 72 6c 20 2d 73 20 2d 6f 20 2f 64 65 76 2f 6e 75 rl -s -o /dev/nu
ac10: 6c 6c 20 2d 77 20 22 46 6f 73 73 69 6c 27 73 20 ll -w "Fossil's
ac20: 70 6f 72 74 20 24 50 20 61 6e 73 77 65 72 73 20 port $P answers
ac30: 6f 6e 20 74 68 65 20 68 6f 73 74 3a 20 25 7b 68 on the host: %{h
ac40: 74 74 70 5f 63 6f 64 65 7d 5c 6e 22 20 68 74 74 ttp_code}\n" htt
ac50: 70 3a 2f 2f 31 32 37 2e 30 2e 30 2e 31 3a 24 50 p://127.0.0.1:$P
ac60: 2f 3b 20 6e 69 63 65 20 2d 6e 20 31 39 20 63 72 /; nice -n 19 cr
ac70: 79 73 74 61 6c 20 62 75 69 6c 64 20 2d 6f 20 24 ystal build -o $
ac80: 50 42 2f 72 65 63 65 69 70 74 73 20 24 4f 55 54 PB/receipts $OUT
ac90: 2f 70 72 6f 62 65 2e 63 72 20 3e 2f 64 65 76 2f /probe.cr >/dev/
aca0: 6e 75 6c 6c 20 26 26 20 74 69 6d 65 6f 75 74 20 null && timeout
acb0: 36 30 20 24 50 42 2f 73 74 61 72 74 2d 6f 63 78 60 $PB/start-ocx
acc0: 20 2d 2d 73 63 72 61 74 63 68 20 24 44 20 72 65 --scratch $D re
acd0: 63 65 69 70 74 73 0a 0a 73 61 79 20 22 74 68 65 ceipts..say "the
ace0: 20 64 61 65 6d 6f 6e 27 73 20 6f 77 6e 20 76 69 daemon's own vi
acf0: 65 77 3a 20 61 20 70 72 6f 62 65 20 69 6e 20 70 ew: a probe in p
ad00: 6c 61 63 65 20 6f 66 20 69 74 73 20 70 72 6f 67 lace of its prog
ad10: 72 61 6d 2c 20 73 74 61 72 74 65 64 20 74 68 72 ram, started thr
ad20: 6f 75 67 68 20 74 68 65 20 73 61 6d 65 20 64 61 ough the same da
ad30: 65 6d 6f 6e 20 70 6f 6c 69 63 79 22 0a 6b 69 6c emon policy".kil
ad40: 6c 20 24 7b 70 69 64 73 5b 32 5d 7d 20 32 3e 2f l ${pids[2]} 2>/
ad50: 64 65 76 2f 6e 75 6c 6c 3b 20 73 6c 65 65 70 20 dev/null; sleep
ad60: 31 0a 6d 6b 64 69 72 20 2d 70 20 24 44 2f 64 61 1.mkdir -p $D/da
ad70: 65 6d 6f 6e 2f 70 72 6f 62 65 30 30 30 30 30 30 emon/probe000000
ad80: 30 31 0a 70 72 69 6e 74 66 20 27 23 21 2f 62 69 01.printf '#!/bi
ad90: 6e 2f 73 68 5c 6e 65 63 68 6f 20 22 64 61 65 6d n/sh\necho "daem
ada0: 6f 6e 20 70 72 6f 62 65 3a 20 75 69 64 3d 24 28 on probe: uid=$(
adb0: 69 64 20 2d 75 29 20 63 61 70 73 3d 24 28 67 72 id -u) caps=$(gr
adc0: 65 70 20 43 61 70 45 66 66 20 2f 70 72 6f 63 2f ep CapEff /proc/
add0: 73 65 6c 66 2f 73 74 61 74 75 73 20 7c 20 74 72 self/status | tr
ade0: 20 2d 73 20 22 5c 74 20 22 20 22 20 22 29 22 5c -s "\t " " ")"\
adf0: 6e 66 6f 72 20 70 20 69 6e 20 25 73 20 25 73 3b nfor p in %s %s;
ae00: 20 64 6f 20 5b 20 2d 65 20 24 70 20 5d 20 26 26 do [ -e $p ] &&
ae10: 20 65 63 68 6f 20 22 56 49 53 49 42 4c 45 20 24 echo "VISIBLE $
ae20: 70 22 20 7c 7c 20 65 63 68 6f 20 22 61 62 73 65 p" || echo "abse
ae30: 6e 74 20 24 70 22 3b 20 64 6f 6e 65 5c 6e 6e 3d nt $p"; done\nn=
ae40: 30 3b 20 66 6f 72 20 78 20 69 6e 20 2f 70 72 6f 0; for x in /pro
ae50: 63 2f 5b 30 2d 39 5d 2a 2f 72 6f 6f 74 25 73 3b c/[0-9]*/root%s;
ae60: 20 64 6f 20 5b 20 2d 72 20 24 78 20 5d 20 26 26 do [ -r $x ] &&
ae70: 20 6e 3d 24 28 28 6e 2b 31 29 29 3b 20 64 6f 6e n=$((n+1)); don
ae80: 65 3b 20 65 63 68 6f 20 22 70 72 6f 63 2d 70 61 e; echo "proc-pa
ae90: 74 68 73 2d 72 65 61 64 61 62 6c 65 3d 24 6e 20 ths-readable=$n
aea0: 28 6f 74 68 65 72 20 70 72 6f 63 65 73 73 65 73 (other processes
aeb0: 20 6f 66 20 74 68 65 20 73 61 6d 65 20 61 63 63 of the same acc
aec0: 6f 75 6e 74 3b 20 74 68 65 20 6c 69 76 65 20 64 ount; the live d
aed0: 61 65 6d 6f 6e 20 68 61 73 20 61 6e 20 61 63 63 aemon has an acc
aee0: 6f 75 6e 74 20 6f 66 20 69 74 73 20 6f 77 6e 29 ount of its own)
aef0: 22 5c 6e 62 77 72 61 70 20 2d 2d 75 6e 73 68 61 "\nbwrap --unsha
af00: 72 65 2d 61 6c 6c 20 2d 2d 72 6f 2d 62 69 6e 64 re-all --ro-bind
af10: 20 2f 75 73 72 20 2f 75 73 72 20 2d 2d 72 6f 2d /usr /usr --ro-
af20: 62 69 6e 64 2d 74 72 79 20 2f 6c 69 62 20 2f 6c bind-try /lib /l
af30: 69 62 20 2d 2d 72 6f 2d 62 69 6e 64 2d 74 72 79 ib --ro-bind-try
af40: 20 2f 6c 69 62 36 34 20 2f 6c 69 62 36 34 20 2d /lib64 /lib64 -
af50: 2d 72 6f 2d 62 69 6e 64 20 2f 62 69 6e 20 2f 62 -ro-bind /bin /b
af60: 69 6e 20 2d 2d 70 72 6f 63 20 2f 70 72 6f 63 20 in --proc /proc
af70: 2d 2d 64 65 76 20 2f 64 65 76 20 2f 62 69 6e 2f --dev /dev /bin/
af80: 73 68 20 2d 63 20 22 65 63 68 6f 20 61 67 65 6e sh -c "echo agen
af90: 74 20 73 61 6e 64 62 6f 78 20 69 6e 73 69 64 65 t sandbox inside
afa0: 3a 20 73 74 61 72 74 65 64 22 20 32 3e 26 31 5c : started" 2>&1\
afb0: 6e 27 20 24 44 2f 66 6f 73 73 69 6c 20 24 52 20 n' $D/fossil $R
afc0: 24 52 20 3e 20 24 44 2f 64 61 65 6d 6f 6e 2f 70 $R > $D/daemon/p
afd0: 72 6f 62 65 30 30 30 30 30 30 30 31 2f 6f 63 78 robe00000001/ocx
afe0: 32 3b 20 63 68 6d 6f 64 20 37 35 35 20 24 44 2f 2; chmod 755 $D/
aff0: 64 61 65 6d 6f 6e 2f 70 72 6f 62 65 30 30 30 30 daemon/probe0000
b000: 30 30 30 31 2f 6f 63 78 32 0a 6c 6e 20 2d 73 66 0001/ocx2.ln -sf
b010: 6e 20 70 72 6f 62 65 30 30 30 30 30 30 30 31 20 n probe00000001
b020: 24 44 2f 64 61 65 6d 6f 6e 2f 63 75 72 72 65 6e $D/daemon/curren
b030: 74 0a 74 69 6d 65 6f 75 74 20 33 30 20 24 42 49 t.timeout 30 $BI
b040: 4e 2f 73 74 61 72 74 2d 6f 63 78 20 2d 2d 73 63 N/start-ocx --sc
b050: 72 61 74 63 68 20 24 44 20 64 61 65 6d 6f 6e 0a ratch $D daemon.
b060: 6c 6e 20 2d 73 66 6e 20 24 52 45 4c 20 24 44 2f ln -sfn $REL $D/
b070: 64 61 65 6d 6f 6e 2f 63 75 72 72 65 6e 74 0a 0a daemon/current..
b080: 73 61 79 20 22 72 65 76 69 65 77 20 74 65 73 74 say "review test
b090: 3a 20 74 68 65 20 2f 72 65 63 65 69 70 74 20 61 : the /receipt a
b0a0: 6c 69 61 73 65 73 20 72 65 61 63 68 20 74 68 65 liases reach the
b0b0: 20 72 65 63 65 69 70 74 20 73 65 72 76 69 63 65 receipt service
b0c0: 2c 20 6e 65 76 65 72 20 74 68 65 20 70 61 73 73 , never the pass
b0d0: 74 68 72 6f 75 67 68 22 0a 66 6f 72 20 70 61 74 through".for pat
b0e0: 68 20 69 6e 20 72 65 63 65 69 70 74 20 72 65 63 h in receipt rec
b0f0: 65 69 70 74 2f 20 72 65 63 65 69 70 74 2f 78 3b eipt/ receipt/x;
b100: 20 64 6f 0a 20 20 63 75 72 6c 20 2d 73 20 2d 64 do. curl -s -d
b110: 20 22 6a 73 6f 6e 3d 6f 6e 26 61 72 74 69 66 61 "json=on&artifa
b120: 63 74 73 3d 24 48 31 22 20 22 24 55 2f 65 78 74 cts=$H1" "$U/ext
b130: 2f 6f 63 78 32 2d 63 67 69 2f 24 70 61 74 68 22 /ocx2-cgi/$path"
b140: 20 7c 20 70 79 74 68 6f 6e 33 20 2d 63 20 27 69 | python3 -c 'i
b150: 6d 70 6f 72 74 20 6a 73 6f 6e 2c 73 79 73 3b 20 mport json,sys;
b160: 61 3d 6a 73 6f 6e 2e 6c 6f 61 64 28 73 79 73 2e a=json.load(sys.
b170: 73 74 64 69 6e 29 3b 20 6f 70 73 3d 5b 6e 65 78 stdin); ops=[nex
b180: 74 28 69 74 65 72 28 6c 29 29 20 69 66 20 69 73 t(iter(l)) if is
b190: 69 6e 73 74 61 6e 63 65 28 6c 2c 64 69 63 74 29 instance(l,dict)
b1a0: 20 61 6e 64 20 6c 65 6e 28 6c 29 3d 3d 31 20 65 and len(l)==1 e
b1b0: 6c 73 65 20 6c 2e 67 65 74 28 22 6f 70 65 72 61 lse l.get("opera
b1c0: 74 69 6f 6e 22 2c 20 6c 29 20 66 6f 72 20 6c 20 tion", l) for l
b1d0: 69 6e 20 61 2e 67 65 74 28 22 6c 6f 67 22 2c 20 in a.get("log",
b1e0: 5b 5d 29 5d 3b 20 70 72 69 6e 74 28 73 79 73 2e [])]; print(sys.
b1f0: 61 72 67 76 5b 31 5d 2c 20 22 2d 3e 22 2c 20 22 argv[1], "->", "
b200: 73 65 72 76 69 63 65 22 20 69 66 20 22 61 73 6b service" if "ask
b210: 5f 72 65 63 65 69 70 74 5f 73 65 72 76 69 63 65 _receipt_service
b220: 22 20 69 6e 20 6a 73 6f 6e 2e 64 75 6d 70 73 28 " in json.dumps(
b230: 61 29 20 65 6c 73 65 20 22 50 41 53 53 54 48 52 a) else "PASSTHR
b240: 4f 55 47 48 22 2c 20 28 61 2e 67 65 74 28 22 72 OUGH", (a.get("r
b250: 65 73 75 6c 74 22 29 20 6f 72 20 7b 7d 29 2e 67 esult") or {}).g
b260: 65 74 28 22 61 72 74 69 66 61 63 74 73 22 2c 20 et("artifacts",
b270: 5b 7b 7d 5d 29 5b 30 5d 2e 67 65 74 28 22 73 65 [{}])[0].get("se
b280: 6e 64 65 72 22 29 29 27 20 22 2f 24 70 61 74 68 nder"))' "/$path
b290: 22 0a 64 6f 6e 65 0a 0a 73 61 79 20 22 72 65 76 ".done..say "rev
b2a0: 69 65 77 20 74 65 73 74 3a 20 6f 6e 65 20 72 65 iew test: one re
b2b0: 70 6f 73 69 74 6f 72 79 20 68 65 6c 64 20 6c 6f pository held lo
b2c0: 63 6b 65 64 20 77 68 69 6c 65 20 61 6e 6f 74 68 cked while anoth
b2d0: 65 72 20 69 73 20 61 73 6b 65 64 2c 20 73 74 72 er is asked, str
b2e0: 61 69 67 68 74 20 61 74 20 74 68 65 20 73 65 72 aight at the ser
b2f0: 76 69 63 65 27 73 20 73 6f 63 6b 65 74 22 0a 63 vice's socket".c
b300: 70 20 24 52 20 24 44 2f 66 6f 73 73 69 6c 2f 64 p $R $D/fossil/d
b310: 65 6d 6f 32 2e 66 6f 73 73 69 6c 0a 70 79 74 68 emo2.fossil.pyth
b320: 6f 6e 33 20 2d 20 24 44 2f 66 6f 73 73 69 6c 2f on3 - $D/fossil/
b330: 64 65 6d 6f 2e 66 6f 73 73 69 6c 20 24 44 2f 72 demo.fossil $D/r
b340: 75 6e 2f 72 65 63 65 69 70 74 73 2e 73 6f 63 6b un/receipts.sock
b350: 20 24 48 31 20 3c 3c 27 50 59 4c 27 0a 69 6d 70 $H1 <<'PYL'.imp
b360: 6f 72 74 20 6a 73 6f 6e 2c 20 73 6f 63 6b 65 74 ort json, socket
b370: 2c 20 73 71 6c 69 74 65 33 2c 20 73 79 73 2c 20 , sqlite3, sys,
b380: 74 68 72 65 61 64 69 6e 67 2c 20 74 69 6d 65 0a threading, time.
b390: 6c 6f 63 6b 65 64 2c 20 73 6f 63 6b 2c 20 68 20 locked, sock, h
b3a0: 3d 20 73 79 73 2e 61 72 67 76 5b 31 3a 34 5d 0a = sys.argv[1:4].
b3b0: 63 6f 6e 20 3d 20 73 71 6c 69 74 65 33 2e 63 6f con = sqlite3.co
b3c0: 6e 6e 65 63 74 28 6c 6f 63 6b 65 64 2c 20 69 73 nnect(locked, is
b3d0: 6f 6c 61 74 69 6f 6e 5f 6c 65 76 65 6c 3d 4e 6f olation_level=No
b3e0: 6e 65 29 3b 20 63 6f 6e 2e 65 78 65 63 75 74 65 ne); con.execute
b3f0: 28 22 42 45 47 49 4e 20 45 58 43 4c 55 53 49 56 ("BEGIN EXCLUSIV
b400: 45 22 29 0a 64 65 66 20 61 73 6b 28 72 65 70 6f E").def ask(repo
b410: 2c 20 6f 75 74 29 3a 0a 20 20 20 20 74 20 3d 20 , out):. t =
b420: 74 69 6d 65 2e 74 69 6d 65 28 29 3b 20 73 20 3d time.time(); s =
b430: 20 73 6f 63 6b 65 74 2e 73 6f 63 6b 65 74 28 73 socket.socket(s
b440: 6f 63 6b 65 74 2e 41 46 5f 55 4e 49 58 29 3b 20 ocket.AF_UNIX);
b450: 73 2e 63 6f 6e 6e 65 63 74 28 73 6f 63 6b 29 0a s.connect(sock).
b460: 20 20 20 20 73 2e 73 65 6e 64 61 6c 6c 28 28 6a s.sendall((j
b470: 73 6f 6e 2e 64 75 6d 70 73 28 7b 22 63 61 70 61 son.dumps({"capa
b480: 62 69 6c 69 74 69 65 73 22 3a 20 22 6f 22 2c 20 bilities": "o",
b490: 22 72 65 70 6f 73 69 74 6f 72 79 22 3a 20 72 65 "repository": re
b4a0: 70 6f 7d 29 20 2b 20 22 5c 6e 22 20 2b 20 6a 73 po}) + "\n" + js
b4b0: 6f 6e 2e 64 75 6d 70 73 28 7b 22 6f 63 78 5f 72 on.dumps({"ocx_r
b4c0: 65 63 65 69 70 74 22 3a 20 31 2c 20 22 61 72 74 eceipt": 1, "art
b4d0: 69 66 61 63 74 73 22 3a 20 5b 68 5d 7d 29 20 2b ifacts": [h]}) +
b4e0: 20 22 5c 6e 22 29 2e 65 6e 63 6f 64 65 28 29 29 "\n").encode())
b4f0: 0a 20 20 20 20 64 61 74 61 20 3d 20 73 2e 6d 61 . data = s.ma
b500: 6b 65 66 69 6c 65 28 29 2e 72 65 61 64 6c 69 6e kefile().readlin
b510: 65 28 29 3b 20 6f 75 74 5b 72 65 70 6f 5d 20 3d e(); out[repo] =
b520: 20 28 72 6f 75 6e 64 28 74 69 6d 65 2e 74 69 6d (round(time.tim
b530: 65 28 29 20 2d 20 74 2c 20 32 29 2c 20 64 61 74 e() - t, 2), dat
b540: 61 2e 73 74 72 69 70 28 29 5b 3a 39 30 5d 29 0a a.strip()[:90]).
b550: 6f 75 74 20 3d 20 7b 7d 0a 74 73 20 3d 20 5b 74 out = {}.ts = [t
b560: 68 72 65 61 64 69 6e 67 2e 54 68 72 65 61 64 28 hreading.Thread(
b570: 74 61 72 67 65 74 3d 61 73 6b 2c 20 61 72 67 73 target=ask, args
b580: 3d 28 72 2c 20 6f 75 74 29 29 20 66 6f 72 20 72 =(r, out)) for r
b590: 20 69 6e 20 28 22 64 65 6d 6f 22 2c 20 22 64 65 in ("demo", "de
b5a0: 6d 6f 32 22 29 5d 0a 5b 74 2e 73 74 61 72 74 28 mo2")].[t.start(
b5b0: 29 20 66 6f 72 20 74 20 69 6e 20 74 73 5d 3b 20 ) for t in ts];
b5c0: 5b 74 2e 6a 6f 69 6e 28 29 20 66 6f 72 20 74 20 [t.join() for t
b5d0: 69 6e 20 74 73 5d 0a 63 6f 6e 2e 72 6f 6c 6c 62 in ts].con.rollb
b5e0: 61 63 6b 28 29 3b 20 63 6f 6e 2e 63 6c 6f 73 65 ack(); con.close
b5f0: 28 29 0a 66 6f 72 20 72 20 69 6e 20 28 22 64 65 ().for r in ("de
b600: 6d 6f 22 2c 20 22 64 65 6d 6f 32 22 29 3a 20 70 mo", "demo2"): p
b610: 72 69 6e 74 28 66 22 7b 72 7d 20 28 6c 6f 63 6b rint(f"{r} (lock
b620: 65 64 3d 7b 72 3d 3d 27 64 65 6d 6f 27 7d 29 3a ed={r=='demo'}):
b630: 20 7b 6f 75 74 5b 72 5d 5b 30 5d 7d 20 73 20 20 {out[r][0]} s
b640: 7b 6f 75 74 5b 72 5d 5b 31 5d 7d 22 29 0a 50 59 {out[r][1]}").PY
b650: 4c 0a 0a 73 61 79 20 22 72 65 76 69 65 77 20 74 L..say "review t
b660: 65 73 74 3a 20 69 6e 73 69 64 65 20 74 68 65 20 est: inside the
b670: 43 47 49 20 73 61 6e 64 62 6f 78 20 28 61 20 70 CGI sandbox (a p
b680: 72 6f 62 65 20 69 6e 20 70 6c 61 63 65 20 6f 66 robe in place of
b690: 20 74 68 65 20 72 65 6c 65 61 73 65 27 73 20 70 the release's p
b6a0: 72 6f 67 72 61 6d 29 22 0a 6d 6b 64 69 72 20 2d rogram)".mkdir -
b6b0: 70 20 24 44 2f 72 65 6c 65 61 73 65 2f 70 72 6f p $D/release/pro
b6c0: 62 65 30 30 30 30 30 30 30 31 0a 70 72 69 6e 74 be00000001.print
b6d0: 66 20 27 23 21 2f 62 69 6e 2f 73 68 5c 6e 50 41 f '#!/bin/sh\nPA
b6e0: 54 48 3d 2f 75 73 72 2f 62 69 6e 3a 2f 62 69 6e TH=/usr/bin:/bin
b6f0: 5c 6e 65 63 68 6f 20 22 61 72 67 73 3a 20 24 2a \necho "args: $*
b700: 22 3b 20 77 68 69 6c 65 20 72 65 61 64 20 6b 20 "; while read k
b710: 76 3b 20 64 6f 20 5b 20 22 24 6b 22 20 3d 20 43 v; do [ "$k" = C
b720: 61 70 45 66 66 3a 20 5d 20 26 26 20 65 63 68 6f apEff: ] && echo
b730: 20 22 43 61 70 45 66 66 3a 20 24 76 22 3b 20 64 "CapEff: $v"; d
b740: 6f 6e 65 20 3c 20 2f 70 72 6f 63 2f 73 65 6c 66 one < /proc/self
b750: 2f 73 74 61 74 75 73 3b 20 66 6f 72 20 78 20 69 /status; for x i
b760: 6e 20 2f 70 72 6f 63 2f 24 24 2f 66 64 2f 2a 3b n /proc/$$/fd/*;
b770: 20 64 6f 20 65 63 68 6f 20 22 66 64 20 24 7b 78 do echo "fd ${x
b780: 23 23 2a 2f 7d 20 2d 3e 20 24 28 72 65 61 64 6c ##*/} -> $(readl
b790: 69 6e 6b 20 24 78 29 22 3b 20 64 6f 6e 65 3b 20 ink $x)"; done;
b7a0: 65 63 68 6f 20 22 63 6f 6f 6b 69 65 3a 20 24 7b echo "cookie: ${
b7b0: 48 54 54 50 5f 43 4f 4f 4b 49 45 3a 2d 61 62 73 HTTP_COOKIE:-abs
b7c0: 65 6e 74 7d 20 6e 6f 6e 63 65 3a 20 24 7b 46 4f ent} nonce: ${FO
b7d0: 53 53 49 4c 5f 4e 4f 4e 43 45 3a 2d 61 62 73 65 SSIL_NONCE:-abse
b7e0: 6e 74 7d 20 72 65 70 6f 73 69 74 6f 72 79 3a 20 nt} repository:
b7f0: 24 46 4f 53 53 49 4c 5f 52 45 50 4f 53 49 54 4f $FOSSIL_REPOSITO
b800: 52 59 22 3b 20 66 6f 72 20 70 20 69 6e 20 25 73 RY"; for p in %s
b810: 20 25 73 3b 20 64 6f 20 5b 20 2d 65 20 24 70 20 %s; do [ -e $p
b820: 5d 20 26 26 20 65 63 68 6f 20 22 56 49 53 49 42 ] && echo "VISIB
b830: 4c 45 20 24 70 22 20 7c 7c 20 65 63 68 6f 20 22 LE $p" || echo "
b840: 61 62 73 65 6e 74 20 24 70 22 3b 20 64 6f 6e 65 absent $p"; done
b850: 3b 20 63 75 72 6c 20 2d 73 20 2d 6d 20 32 20 2d ; curl -s -m 2 -
b860: 6f 20 2f 64 65 76 2f 6e 75 6c 6c 20 2d 77 20 22 o /dev/null -w "
b870: 6e 65 74 77 6f 72 6b 3a 20 25 25 7b 68 74 74 70 network: %%{http
b880: 5f 63 6f 64 65 7d 5c 5c 6e 22 20 68 74 74 70 3a _code}\\n" http:
b890: 2f 2f 31 32 37 2e 30 2e 30 2e 31 3a 25 73 2f 20 //127.0.0.1:%s/
b8a0: 7c 7c 20 65 63 68 6f 20 22 6e 65 74 77 6f 72 6b || echo "network
b8b0: 3a 20 72 65 66 75 73 65 64 22 5c 6e 27 20 24 44 : refused"\n' $D
b8c0: 2f 66 6f 73 73 69 6c 20 24 52 20 24 50 20 3e 20 /fossil $R $P >
b8d0: 24 44 2f 72 65 6c 65 61 73 65 2f 70 72 6f 62 65 $D/release/probe
b8e0: 30 30 30 30 30 30 30 31 2f 6f 63 78 32 3b 20 63 00000001/ocx2; c
b8f0: 68 6d 6f 64 20 37 35 35 20 24 44 2f 72 65 6c 65 hmod 755 $D/rele
b900: 61 73 65 2f 70 72 6f 62 65 30 30 30 30 30 30 30 ase/probe0000000
b910: 31 2f 6f 63 78 32 0a 6c 6e 20 2d 73 66 6e 20 70 1/ocx2.ln -sfn p
b920: 72 6f 62 65 30 30 30 30 30 30 30 31 20 24 44 2f robe00000001 $D/
b930: 72 65 6c 65 61 73 65 2f 63 75 72 72 65 6e 74 0a release/current.
b940: 65 6e 76 20 2d 69 20 50 41 54 48 5f 49 4e 46 4f env -i PATH_INFO
b950: 3d 2f 72 65 63 65 69 70 74 20 46 4f 53 53 49 4c =/receipt FOSSIL
b960: 5f 52 45 50 4f 53 49 54 4f 52 59 3d 24 52 20 46 _REPOSITORY=$R F
b970: 4f 53 53 49 4c 5f 43 41 50 41 42 49 4c 49 54 49 OSSIL_CAPABILITI
b980: 45 53 3d 6f 20 48 54 54 50 5f 43 4f 4f 4b 49 45 ES=o HTTP_COOKIE
b990: 3d 73 65 63 72 65 74 2d 63 6f 6f 6b 69 65 20 46 =secret-cookie F
b9a0: 4f 53 53 49 4c 5f 4e 4f 4e 43 45 3d 6e 31 32 33 OSSIL_NONCE=n123
b9b0: 20 52 45 51 55 45 53 54 5f 4d 45 54 48 4f 44 3d REQUEST_METHOD=
b9c0: 47 45 54 20 62 61 73 68 20 2d 63 20 22 65 78 65 GET bash -c "exe
b9d0: 63 20 2d 61 20 6f 63 78 32 2d 63 67 69 20 24 42 c -a ocx2-cgi $B
b9e0: 49 4e 2f 73 74 61 72 74 2d 6f 63 78 20 2d 2d 73 IN/start-ocx --s
b9f0: 63 72 61 74 63 68 20 24 44 22 20 32 3e 26 31 20 cratch $D" 2>&1
ba00: 7c 20 68 65 61 64 20 2d 31 32 0a 6c 6e 20 2d 73 | head -12.ln -s
ba10: 66 6e 20 24 52 45 4c 20 24 44 2f 72 65 6c 65 61 fn $REL $D/relea
ba20: 73 65 2f 63 75 72 72 65 6e 74 0a 0a 73 61 79 20 se/current..say
ba30: 22 72 65 76 69 65 77 20 74 65 73 74 3a 20 61 20 "review test: a
ba40: 72 65 63 65 69 70 74 20 73 65 72 76 69 63 65 20 receipt service
ba50: 74 68 61 74 20 6c 69 73 74 65 6e 73 20 62 75 74 that listens but
ba60: 20 6e 65 76 65 72 20 61 63 63 65 70 74 73 22 0a never accepts".
ba70: 6b 69 6c 6c 20 24 7b 70 69 64 73 5b 30 5d 7d 20 kill ${pids[0]}
ba80: 32 3e 2f 64 65 76 2f 6e 75 6c 6c 3b 20 73 6c 65 2>/dev/null; sle
ba90: 65 70 20 31 3b 20 72 6d 20 2d 66 20 24 44 2f 72 ep 1; rm -f $D/r
baa0: 75 6e 2f 72 65 63 65 69 70 74 73 2e 73 6f 63 6b un/receipts.sock
bab0: 0a 70 79 74 68 6f 6e 33 20 2d 20 24 44 2f 72 75 .python3 - $D/ru
bac0: 6e 2f 72 65 63 65 69 70 74 73 2e 73 6f 63 6b 20 n/receipts.sock
bad0: 3c 3c 27 50 59 53 27 20 26 0a 69 6d 70 6f 72 74 <<'PYS' &.import
bae0: 20 73 6f 63 6b 65 74 2c 20 73 79 73 2c 20 74 69 socket, sys, ti
baf0: 6d 65 0a 73 20 3d 20 73 6f 63 6b 65 74 2e 73 6f me.s = socket.so
bb00: 63 6b 65 74 28 73 6f 63 6b 65 74 2e 41 46 5f 55 cket(socket.AF_U
bb10: 4e 49 58 29 3b 20 73 2e 62 69 6e 64 28 73 79 73 NIX); s.bind(sys
bb20: 2e 61 72 67 76 5b 31 5d 29 3b 20 73 2e 6c 69 73 .argv[1]); s.lis
bb30: 74 65 6e 28 30 29 0a 68 65 6c 64 20 3d 20 5b 5d ten(0).held = []
bb40: 0a 77 68 69 6c 65 20 54 72 75 65 3a 0a 20 20 20 .while True:.
bb50: 20 63 20 3d 20 73 6f 63 6b 65 74 2e 73 6f 63 6b c = socket.sock
bb60: 65 74 28 73 6f 63 6b 65 74 2e 41 46 5f 55 4e 49 et(socket.AF_UNI
bb70: 58 29 3b 20 63 2e 73 65 74 62 6c 6f 63 6b 69 6e X); c.setblockin
bb80: 67 28 46 61 6c 73 65 29 0a 20 20 20 20 74 72 79 g(False). try
bb90: 3a 20 63 2e 63 6f 6e 6e 65 63 74 28 73 79 73 2e : c.connect(sys.
bba0: 61 72 67 76 5b 31 5d 29 3b 20 68 65 6c 64 2e 61 argv[1]); held.a
bbb0: 70 70 65 6e 64 28 63 29 0a 20 20 20 20 65 78 63 ppend(c). exc
bbc0: 65 70 74 20 28 42 6c 6f 63 6b 69 6e 67 49 4f 45 ept (BlockingIOE
bbd0: 72 72 6f 72 2c 20 4f 53 45 72 72 6f 72 29 3a 20 rror, OSError):
bbe0: 62 72 65 61 6b 0a 74 69 6d 65 2e 73 6c 65 65 70 break.time.sleep
bbf0: 28 33 30 29 0a 50 59 53 0a 53 54 41 4c 4c 3d 24 (30).PYS.STALL=$
bc00: 21 3b 20 73 6c 65 65 70 20 31 0a 74 30 3d 24 28 !; sleep 1.t0=$(
bc10: 64 61 74 65 20 2b 25 73 2e 25 4e 29 3b 20 63 75 date +%s.%N); cu
bc20: 72 6c 20 2d 73 20 2d 6d 20 32 30 20 2d 6f 20 24 rl -s -m 20 -o $
bc30: 4f 55 54 2f 73 74 61 6c 6c 65 64 2e 74 78 74 20 OUT/stalled.txt
bc40: 2d 77 20 22 73 74 61 74 75 73 20 25 7b 68 74 74 -w "status %{htt
bc50: 70 5f 63 6f 64 65 7d 22 20 2d 64 20 22 6a 73 6f p_code}" -d "jso
bc60: 6e 3d 6f 6e 26 61 72 74 69 66 61 63 74 73 3d 24 n=on&artifacts=$
bc70: 48 31 22 20 22 24 55 2f 65 78 74 2f 6f 63 78 32 H1" "$U/ext/ocx2
bc80: 2d 63 67 69 2f 72 65 63 65 69 70 74 22 3b 20 65 -cgi/receipt"; e
bc90: 63 68 6f 20 22 20 61 66 74 65 72 20 24 28 65 63 cho " after $(ec
bca0: 68 6f 20 22 24 28 64 61 74 65 20 2b 25 73 2e 25 ho "$(date +%s.%
bcb0: 4e 29 20 2d 20 24 74 30 22 20 7c 20 62 63 20 7c N) - $t0" | bc |
bcc0: 20 63 75 74 20 2d 63 31 2d 34 29 20 73 3a 20 24 cut -c1-4) s: $
bcd0: 28 68 65 61 64 20 2d 63 20 31 32 30 20 24 4f 55 (head -c 120 $OU
bce0: 54 2f 73 74 61 6c 6c 65 64 2e 74 78 74 29 22 0a T/stalled.txt)".
bcf0: 6b 69 6c 6c 20 24 53 54 41 4c 4c 20 32 3e 2f 64 kill $STALL 2>/d
bd00: 65 76 2f 6e 75 6c 6c 0a 0a 73 61 79 20 22 72 6f ev/null..say "ro
bd10: 6c 6c 62 61 63 6b 20 70 6f 69 6e 74 3a 20 74 68 llback point: th
bd20: 65 20 6c 61 75 6e 63 68 65 72 27 73 20 43 47 49 e launcher's CGI
bd30: 20 6c 69 6e 6b 20 69 73 20 6f 6e 65 20 66 69 6c link is one fil
bd40: 65 3b 20 74 68 65 20 70 72 65 76 69 6f 75 73 20 e; the previous
bd50: 70 61 67 65 20 69 73 20 74 68 65 20 72 65 6c 65 page is the rele
bd60: 61 73 65 27 73 20 6f 77 6e 20 6f 63 78 32 22 0a ase's own ocx2".
bd70: 6c 73 20 2d 6c 20 24 44 2f 65 78 74 2f 6f 63 78 ls -l $D/ext/ocx
bd80: 32 2d 63 67 69 0a 65 63 68 6f 20 22 64 6f 6e 65 2-cgi.echo "done
bd90: 22 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "...............
bda0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bdb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bdc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bdd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bde0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bdf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
be00: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69 scripts-for-revi
be10: 65 77 2d 32 2f 73 74 61 67 65 41 2d 66 69 78 65 ew-2/stageA-fixe
be20: 73 2d 70 6c 61 6e 2e 6d 64 00 00 00 00 00 00 00 s-plan.md.......
be30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
be40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
be50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
be60: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30 ....0000644.0000
be70: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 000.0000000.0000
be80: 30 30 30 35 35 36 34 00 31 35 32 37 30 35 32 33 0005564.15270523
be90: 31 32 32 00 30 31 37 32 37 30 00 20 30 00 00 00 122.017270. 0...
bea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
beb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bec0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bed0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bef0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bf00: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00 .ustar .root...
bf10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bf20: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00 .........root...
bf30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bf40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bf50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bf60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bf70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bf80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bf90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bfa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bfb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bfc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bfd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bfe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
bff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
c000: 23 20 53 74 61 67 65 20 41 3a 20 66 69 78 65 73 # Stage A: fixes
c010: 20 66 72 6f 6d 20 53 6c 6f 77 70 65 6e 27 73 20 from Slowpen's
c020: 72 65 76 69 65 77 20 6f 66 20 74 68 65 20 74 77 review of the tw
c030: 6f 20 73 63 72 69 70 74 73 2f 20 70 72 6f 67 72 o scripts/ progr
c040: 61 6d 73 0a 0a 45 64 69 74 20 6f 6e 6c 79 20 60 ams..Edit only `
c050: 73 63 72 69 70 74 73 2f 73 74 61 72 74 2d 6f 63 scripts/start-oc
c060: 78 2e 63 72 60 20 61 6e 64 20 60 73 63 72 69 70 x.cr` and `scrip
c070: 74 73 2f 72 65 63 65 69 70 74 73 2e 63 72 60 2e ts/receipts.cr`.
c080: 20 42 6f 74 68 20 61 72 65 20 73 74 69 6c 6c 0a Both are still.
c090: 75 6e 63 6f 6d 6d 69 74 74 65 64 20 61 6e 64 20 uncommitted and
c0a0: 75 6e 64 65 72 20 74 68 65 20 6d 61 69 6e 74 61 under the mainta
c0b0: 69 6e 65 72 27 73 20 72 65 76 69 65 77 20 28 64 iner's review (d
c0c0: 65 63 69 73 69 6f 6e 20 32 32 29 2e 20 4b 65 65 ecision 22). Kee
c0d0: 70 20 65 61 63 68 0a 73 65 6c 66 2d 63 6f 6e 74 p each.self-cont
c0e0: 61 69 6e 65 64 20 28 73 74 61 6e 64 61 72 64 20 ained (standard
c0f0: 6c 69 62 72 61 72 79 20 6f 6e 6c 79 29 2c 20 73 library only), s
c100: 6d 61 6c 6c 20 61 6e 64 20 63 6f 6d 6d 65 6e 74 mall and comment
c110: 65 64 20 77 68 65 72 65 20 61 20 72 65 61 64 65 ed where a reade
c120: 72 0a 77 6f 75 6c 64 20 61 73 6b 20 77 68 79 2e r.would ask why.
c130: 20 4e 6f 20 6e 65 77 20 6d 65 63 68 61 6e 69 73 No new mechanis
c140: 6d 73 20 62 65 79 6f 6e 64 20 77 68 61 74 20 65 ms beyond what e
c150: 61 63 68 20 66 69 78 20 6e 65 65 64 73 2e 0a 0a ach fix needs...
c160: 23 23 20 31 2e 20 73 74 61 72 74 2d 6f 63 78 3a ## 1. start-ocx:
c170: 20 62 6f 75 6e 64 20 74 68 65 20 63 6f 6e 6e 65 bound the conne
c180: 63 74 69 6f 6e 20 61 6e 64 20 74 68 65 20 69 64 ction and the id
c190: 65 6e 74 69 74 79 20 77 72 69 74 65 20 28 6c 69 entity write (li
c1a0: 6e 65 73 20 7e 31 32 36 e2 80 93 31 34 33 29 0a nes ~126–143).
c1b0: 0a 42 65 66 6f 72 65 20 60 63 6f 6e 6e 65 63 74 .Before `connect
c1c0: 60 2c 20 73 65 74 20 60 53 4f 5f 53 4e 44 54 49 `, set `SO_SNDTI
c1d0: 4d 45 4f 60 20 74 6f 20 32 20 73 65 63 6f 6e 64 MEO` to 2 second
c1e0: 73 20 6f 6e 20 74 68 65 20 73 6f 63 6b 65 74 20 s on the socket
c1f0: 77 69 74 68 0a 60 73 65 74 73 6f 63 6b 6f 70 74 with.`setsockopt
c200: 60 2c 20 64 65 63 6c 61 72 69 6e 67 20 74 68 65 `, declaring the
c210: 20 60 74 69 6d 65 76 61 6c 60 20 73 74 72 75 63 `timeval` struc
c220: 74 20 69 6e 20 61 20 73 6d 61 6c 6c 20 60 6c 69 t in a small `li
c230: 62 60 20 62 69 6e 64 69 6e 67 20 69 66 0a 6e 65 b` binding if.ne
c240: 65 64 65 64 2e 20 4c 69 6e 75 78 20 61 70 70 6c eded. Linux appl
c250: 69 65 73 20 60 53 4f 5f 53 4e 44 54 49 4d 45 4f ies `SO_SNDTIMEO
c260: 60 20 74 6f 20 61 20 55 6e 69 78 20 73 74 72 65 ` to a Unix stre
c270: 61 6d 20 73 6f 63 6b 65 74 27 73 20 60 63 6f 6e am socket's `con
c280: 6e 65 63 74 60 0a 77 68 65 6e 20 74 68 65 20 6c nect`.when the l
c290: 69 73 74 65 6e 65 72 27 73 20 71 75 65 75 65 20 istener's queue
c2a0: 69 73 20 66 75 6c 6c 2c 20 61 6e 64 20 74 6f 20 is full, and to
c2b0: 60 77 72 69 74 65 60 2e 20 41 20 63 6f 6e 6e 65 `write`. A conne
c2c0: 63 74 20 6f 72 20 77 72 69 74 65 20 74 68 61 74 ct or write that
c2d0: 0a 74 69 6d 65 73 20 6f 75 74 20 28 45 41 47 41 .times out (EAGA
c2e0: 49 4e 2f 45 57 4f 55 4c 44 42 4c 4f 43 4b 2c 20 IN/EWOULDBLOCK,
c2f0: 6f 72 20 45 49 4e 50 52 4f 47 52 45 53 53 29 20 or EINPROGRESS)
c300: 6f 72 20 66 61 69 6c 73 20 69 6e 20 61 6e 79 20 or fails in any
c310: 77 61 79 20 67 69 76 65 73 0a 74 68 65 20 35 30 way gives.the 50
c320: 33 20 61 6e 73 77 65 72 20 74 68 61 74 20 69 73 3 answer that is
c330: 20 61 6c 72 65 61 64 79 20 74 68 65 72 65 2e 20 already there.
c340: 57 72 69 74 65 20 61 20 73 68 6f 72 74 20 63 6f Write a short co
c350: 6d 6d 65 6e 74 20 73 61 79 69 6e 67 20 77 68 79 mment saying why
c360: 2e 0a 0a 23 23 20 32 2e 20 73 74 61 72 74 2d 6f ...## 2. start-o
c370: 63 78 3a 20 72 6f 75 74 65 20 60 72 65 63 65 69 cx: route `recei
c380: 70 74 60 20 65 78 61 63 74 6c 79 20 61 73 20 74 pt` exactly as t
c390: 68 65 20 43 47 49 20 72 65 61 64 73 20 61 20 70 he CGI reads a p
c3a0: 61 74 68 20 28 6c 69 6e 65 73 20 7e 31 31 35 e2 ath (lines ~115â
c3b0: 80 93 31 32 30 29 0a 0a 54 68 65 20 43 47 49 20 €“120)..The CGI
c3c0: 70 69 63 6b 73 20 74 68 65 20 74 6f 6f 6c 20 6c picks the tool l
c3d0: 69 6b 65 20 74 68 69 73 20 28 73 72 63 2f 6f 63 ike this (src/oc
c3e0: 78 2f 63 67 69 2e 63 72 29 3a 0a 60 77 6f 72 64 x/cgi.cr):.`word
c3f0: 73 20 3d 20 50 41 54 48 5f 49 4e 46 4f 2e 73 70 s = PATH_INFO.sp
c400: 6c 69 74 28 27 2f 27 29 2e 72 65 6a 65 63 74 28 lit('/').reject(
c410: 26 2e 65 6d 70 74 79 3f 29 60 2c 20 74 68 65 6e &.empty?)`, then
c420: 0a 60 6e 61 6d 65 20 3d 20 77 6f 72 64 73 5b 28 .`name = words[(
c430: 28 77 6f 72 64 73 2e 72 69 6e 64 65 78 28 70 72 (words.rindex(pr
c440: 6f 67 72 61 6d 29 20 7c 7c 20 2d 31 29 20 2b 20 ogram) || -1) +
c450: 31 29 2e 2e 5d 2e 66 69 72 73 74 3f 60 2c 20 77 1)..].first?`, w
c460: 68 65 72 65 0a 60 70 72 6f 67 72 61 6d 60 20 69 here.`program` i
c470: 73 20 74 68 65 20 70 72 6f 67 72 61 6d 27 73 20 s the program's
c480: 62 61 73 65 6e 61 6d 65 20 28 60 6f 63 78 32 2d basename (`ocx2-
c490: 63 67 69 60 29 2e 20 55 73 65 20 74 68 65 20 73 cgi`). Use the s
c4a0: 61 6d 65 20 72 75 6c 65 20 69 6e 20 74 68 65 0a ame rule in the.
c4b0: 6c 61 75 6e 63 68 65 72 2e 20 54 68 65 20 72 65 launcher. The re
c4c0: 6c 61 79 20 62 72 61 6e 63 68 20 72 75 6e 73 20 lay branch runs
c4d0: 77 68 65 6e 20 60 6e 61 6d 65 20 3d 3d 20 22 72 when `name == "r
c4e0: 65 63 65 69 70 74 22 60 2c 20 73 6f 20 60 2f 72 eceipt"`, so `/r
c4f0: 65 63 65 69 70 74 2f 60 2c 0a 60 2f 72 65 63 65 eceipt/`,.`/rece
c500: 69 70 74 2f 78 60 20 61 6e 64 20 60 2f 6f 63 78 ipt/x` and `/ocx
c510: 32 2d 63 67 69 2f 72 65 63 65 69 70 74 60 20 61 2-cgi/receipt` a
c520: 6c 6c 20 72 65 61 63 68 20 74 68 65 20 73 65 72 ll reach the ser
c530: 76 69 63 65 2c 20 6e 65 76 65 72 20 74 68 65 0a vice, never the.
c540: 70 61 73 73 74 68 72 6f 75 67 68 2e 20 57 72 69 passthrough. Wri
c550: 74 65 20 61 20 73 68 6f 72 74 20 63 6f 6d 6d 65 te a short comme
c560: 6e 74 20 6e 61 6d 69 6e 67 20 73 72 63 2f 6f 63 nt naming src/oc
c570: 78 2f 63 67 69 2e 63 72 20 61 73 20 74 68 65 20 x/cgi.cr as the
c580: 72 75 6c 65 20 69 74 0a 6d 69 72 72 6f 72 73 2e rule it.mirrors.
c590: 0a 0a 23 23 20 33 2e 20 73 74 61 72 74 2d 6f 63 ..## 3. start-oc
c5a0: 78 3a 20 64 72 6f 70 20 65 76 65 72 79 20 63 61 x: drop every ca
c5b0: 70 61 62 69 6c 69 74 79 20 69 6e 20 74 68 65 20 pability in the
c5c0: 43 47 49 27 73 20 73 61 6e 64 62 6f 78 0a 0a 41 CGI's sandbox..A
c5d0: 64 64 20 60 2d 2d 63 61 70 2d 64 72 6f 70 20 41 dd `--cap-drop A
c5e0: 4c 4c 60 20 74 6f 20 60 63 67 69 5f 61 72 67 73 LL` to `cgi_args
c5f0: 60 2c 20 61 73 20 60 72 65 63 65 69 70 74 73 5f `, as `receipts_
c600: 61 72 67 73 60 20 68 61 73 2e 0a 0a 23 23 20 34 args` has...## 4
c610: 2e 20 72 65 63 65 69 70 74 73 3a 20 61 20 64 61 . receipts: a da
c620: 74 61 62 61 73 65 20 6c 6f 63 6b 20 6d 75 73 74 tabase lock must
c630: 20 6e 6f 74 20 73 74 61 6c 6c 20 6f 74 68 65 72 not stall other
c640: 20 72 65 71 75 65 73 74 73 20 28 6c 69 6e 65 73 requests (lines
c650: 20 7e 31 31 30 e2 80 93 31 34 35 2c 20 32 34 36 ~110–145, 246
c660: e2 80 93 32 39 39 29 0a 0a 54 68 65 20 70 72 6f –299)..The pro
c670: 67 72 61 6d 20 69 73 20 6f 6e 65 20 74 68 72 65 gram is one thre
c680: 61 64 2c 20 73 6f 20 53 51 4c 69 74 65 27 73 20 ad, so SQLite's
c690: 62 75 73 79 20 68 61 6e 64 6c 65 72 20 73 6c 65 busy handler sle
c6a0: 65 70 69 6e 67 20 69 6e 73 69 64 65 20 43 0a 62 eping inside C.b
c6b0: 6c 6f 63 6b 73 20 65 76 65 72 79 20 66 69 62 65 locks every fibe
c6c0: 72 2e 20 4d 61 6b 65 20 77 61 69 74 69 6e 67 20 r. Make waiting
c6d0: 68 61 70 70 65 6e 20 69 6e 20 43 72 79 73 74 61 happen in Crysta
c6e0: 6c 20 69 6e 73 74 65 61 64 3a 0a 2d 20 53 65 74 l instead:.- Set
c6f0: 20 60 73 71 6c 69 74 65 33 5f 62 75 73 79 5f 74 `sqlite3_busy_t
c700: 69 6d 65 6f 75 74 28 64 62 2c 20 30 29 60 2c 20 imeout(db, 0)`,
c710: 73 6f 20 61 20 6c 6f 63 6b 65 64 20 64 61 74 61 so a locked data
c720: 62 61 73 65 20 72 65 74 75 72 6e 73 0a 20 20 53 base returns. S
c730: 51 4c 49 54 45 5f 42 55 53 59 20 28 6f 72 20 53 QLITE_BUSY (or S
c740: 51 4c 49 54 45 5f 4c 4f 43 4b 45 44 29 20 61 74 QLITE_LOCKED) at
c750: 20 6f 6e 63 65 2e 0a 2d 20 49 6e 20 60 73 65 72 once..- In `ser
c760: 76 65 60 2c 20 72 75 6e 20 74 68 65 20 77 68 6f ve`, run the who
c770: 6c 65 20 72 65 61 64 20 28 6f 70 65 6e 2c 20 60 le read (open, `
c780: 71 75 65 72 79 5f 6f 6e 6c 79 60 2c 20 60 42 45 query_only`, `BE
c790: 47 49 4e 60 2c 20 74 68 65 0a 20 20 71 75 65 72 GIN`, the. quer
c7a0: 69 65 73 2c 20 60 43 4f 4d 4d 49 54 60 2c 20 63 ies, `COMMIT`, c
c7b0: 6c 6f 73 65 29 20 61 73 20 6f 6e 65 20 61 74 74 lose) as one att
c7c0: 65 6d 70 74 2e 20 49 66 20 61 6e 20 61 74 74 65 empt. If an atte
c7d0: 6d 70 74 20 72 61 69 73 65 73 0a 20 20 60 55 6e mpt raises. `Un
c7e0: 61 76 61 69 6c 61 62 6c 65 60 20 77 68 6f 73 65 available` whose
c7f0: 20 6e 61 6d 65 20 73 74 61 72 74 73 20 77 69 74 name starts wit
c800: 68 20 60 53 51 4c 49 54 45 5f 42 55 53 59 60 20 h `SQLITE_BUSY`
c810: 6f 72 20 60 53 51 4c 49 54 45 5f 4c 4f 43 4b 45 or `SQLITE_LOCKE
c820: 44 60 2c 0a 20 20 63 6c 6f 73 65 20 61 6e 64 20 D`,. close and
c830: 60 73 6c 65 65 70 20 35 30 2e 6d 69 6c 6c 69 73 `sleep 50.millis
c840: 65 63 6f 6e 64 73 60 20 28 77 68 69 63 68 20 79 econds` (which y
c850: 69 65 6c 64 73 20 74 6f 20 6f 74 68 65 72 20 66 ields to other f
c860: 69 62 65 72 73 29 2e 20 54 68 65 6e 0a 20 20 74 ibers). Then. t
c870: 72 79 20 61 67 61 69 6e 2c 20 75 6e 74 69 6c 20 ry again, until
c880: 32 20 73 65 63 6f 6e 64 73 20 68 61 76 65 20 70 2 seconds have p
c890: 61 73 73 65 64 20 73 69 6e 63 65 20 74 68 65 20 assed since the
c8a0: 66 69 72 73 74 20 61 74 74 65 6d 70 74 2e 20 54 first attempt. T
c8b0: 68 65 6e 0a 20 20 61 6e 73 77 65 72 20 60 75 6e hen. answer `un
c8c0: 61 76 61 69 6c 61 62 6c 65 60 20 77 69 74 68 20 available` with
c8d0: 74 68 65 20 6c 61 73 74 20 65 72 72 6f 72 2e 0a the last error..
c8e0: 2d 20 41 6e 79 20 6f 74 68 65 72 20 60 55 6e 61 - Any other `Una
c8f0: 76 61 69 6c 61 62 6c 65 60 2c 20 73 75 63 68 20 vailable`, such
c900: 61 73 20 52 45 41 44 4f 4e 4c 59 5f 52 4f 4c 4c as READONLY_ROLL
c910: 42 41 43 4b 20 66 6f 72 20 61 20 68 6f 74 20 6a BACK for a hot j
c920: 6f 75 72 6e 61 6c 2c 0a 20 20 61 6e 73 77 65 72 ournal,. answer
c930: 73 20 61 74 20 6f 6e 63 65 2c 20 61 73 20 6e 6f s at once, as no
c940: 77 2e 0a 2d 20 54 68 65 20 77 68 6f 6c 65 20 63 w..- The whole c
c950: 6f 6e 6e 65 63 74 69 6f 6e 2c 20 66 72 6f 6d 20 onnection, from
c960: 61 63 63 65 70 74 20 74 6f 20 61 6e 73 77 65 72 accept to answer
c970: 2c 20 69 73 20 62 6f 75 6e 64 65 64 20 61 74 20 , is bounded at
c980: 35 20 73 65 63 6f 6e 64 73 3a 0a 20 20 32 20 66 5 seconds:. 2 f
c990: 6f 72 20 69 74 73 20 74 77 6f 20 6c 69 6e 65 73 or its two lines
c9a0: 2c 20 74 68 65 6e 20 61 74 20 6d 6f 73 74 20 32 , then at most 2
c9b0: 20 77 61 69 74 69 6e 67 20 6f 6e 20 61 20 6c 6f waiting on a lo
c9c0: 63 6b 2e 20 54 68 65 20 73 6c 6f 74 20 69 73 0a ck. The slot is.
c9d0: 20 20 72 65 6c 65 61 73 65 64 20 69 6e 20 60 68 released in `h
c9e0: 61 6e 64 6c 65 60 27 73 20 60 65 6e 73 75 72 65 andle`'s `ensure
c9f0: 60 2c 20 61 73 20 6e 6f 77 2e 0a 0a 57 72 69 74 `, as now...Writ
ca00: 65 20 61 20 63 6f 6d 6d 65 6e 74 20 73 61 79 69 e a comment sayi
ca10: 6e 67 20 77 68 79 20 77 61 69 74 69 6e 67 20 69 ng why waiting i
ca20: 73 20 69 6e 20 43 72 79 73 74 61 6c 2e 0a 0a 23 s in Crystal...#
ca30: 23 20 44 6f 6e 65 20 77 68 65 6e 0a 0a 2d 20 60 # Done when..- `
ca40: 63 72 79 73 74 61 6c 20 62 75 69 6c 64 20 2d 2d crystal build --
ca50: 6e 6f 2d 63 6f 64 65 67 65 6e 60 20 61 6e 64 20 no-codegen` and
ca60: 60 63 72 79 73 74 61 6c 20 74 6f 6f 6c 20 66 6f `crystal tool fo
ca70: 72 6d 61 74 20 2d 2d 63 68 65 63 6b 60 20 70 61 rmat --check` pa
ca80: 73 73 20 66 6f 72 0a 20 20 62 6f 74 68 20 66 69 ss for. both fi
ca90: 6c 65 73 2e 0a 2d 20 50 72 69 6e 74 20 74 68 65 les..- Print the
caa0: 20 66 75 6c 6c 20 64 69 66 66 2e 0a 0a 54 68 65 full diff...The
cab0: 20 72 65 76 69 65 77 65 72 20 72 75 6e 73 20 74 reviewer runs t
cac0: 68 65 20 73 63 72 61 74 63 68 20 64 65 70 6c 6f he scratch deplo
cad0: 79 6d 65 6e 74 2e 20 49 74 20 74 65 73 74 73 3a yment. It tests:
cae0: 0a 2d 20 61 20 6c 69 73 74 65 6e 65 72 20 74 68 .- a listener th
caf0: 61 74 20 6e 65 76 65 72 20 61 63 63 65 70 74 73 at never accepts
cb00: 3b 0a 2d 20 74 68 65 20 60 2f 72 65 63 65 69 70 ;.- the `/receip
cb10: 74 2f 60 20 61 6c 69 61 73 65 73 3b 0a 2d 20 74 t/` aliases;.- t
cb20: 68 65 20 43 47 49 20 73 61 6e 64 62 6f 78 27 73 he CGI sandbox's
cb30: 20 63 61 70 61 62 69 6c 69 74 69 65 73 3b 0a 2d capabilities;.-
cb40: 20 6f 6e 65 20 72 65 70 6f 73 69 74 6f 72 79 20 one repository
cb50: 68 65 6c 64 20 6c 6f 63 6b 65 64 20 77 68 69 6c held locked whil
cb60: 65 20 61 6e 6f 74 68 65 72 20 69 73 20 61 73 6b e another is ask
cb70: 65 64 2e 0a 00 00 00 00 00 00 00 00 00 00 00 00 ed..............
cb80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cb90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cbb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cbc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cbd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cbe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cbf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cc00: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69 scripts-for-revi
cc10: 65 77 2d 32 2f 73 68 61 72 64 2e 79 6d 6c 2e 64 ew-2/shard.yml.d
cc20: 69 66 66 00 00 00 00 00 00 00 00 00 00 00 00 00 iff.............
cc30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cc40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cc50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cc60: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30 ....0000644.0000
cc70: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 000.0000000.0000
cc80: 30 30 30 31 33 35 32 00 31 35 32 37 30 35 32 33 0001352.15270523
cc90: 31 32 32 00 30 31 36 32 32 30 00 20 30 00 00 00 122.016220. 0...
cca0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
ccb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
ccc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
ccd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
ccf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cd00: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00 .ustar .root...
cd10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cd20: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00 .........root...
cd30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cd40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cd50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cd60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cd70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cd90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cda0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cdb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cdc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cdd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cde0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
cdf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
ce00: 49 6e 64 65 78 3a 20 73 68 61 72 64 2e 79 6d 6c Index: shard.yml
ce10: 0a 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d .===============
ce20: 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d ================
ce30: 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d ================
ce40: 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d ================
ce50: 3d 3d 3d 0a 2d 2d 2d 20 73 68 61 72 64 2e 79 6d ===.--- shard.ym
ce60: 6c 0a 2b 2b 2b 20 73 68 61 72 64 2e 79 6d 6c 0a l.+++ shard.yml.
ce70: 40 40 20 2d 31 30 2c 31 34 20 2b 31 30 2c 31 38 @@ -10,14 +10,18
ce80: 20 40 40 0a 20 20 20 73 71 6c 69 74 65 33 3a 0a @@. sqlite3:.
ce90: 20 20 20 20 20 67 69 74 68 75 62 3a 20 63 72 79 github: cry
cea0: 73 74 61 6c 2d 6c 61 6e 67 2f 63 72 79 73 74 61 stal-lang/crysta
ceb0: 6c 2d 73 71 6c 69 74 65 33 0a 20 20 20 20 20 76 l-sqlite3. v
cec0: 65 72 73 69 6f 6e 3a 20 30 2e 32 33 2e 30 0a 20 ersion: 0.23.0.
ced0: 0a 20 23 20 54 68 65 20 62 75 69 6c 64 20 73 74 . # The build st
cee0: 65 70 3a 20 60 73 68 61 72 64 73 20 62 75 69 6c ep: `shards buil
cef0: 64 60 20 70 75 74 73 20 74 68 65 73 65 20 69 6e d` puts these in
cf00: 20 62 69 6e 2f 2e 20 53 69 6e 63 65 20 73 6c 69 bin/. Since sli
cf10: 63 65 20 31 20 4f 43 58 20 69 73 0a 2d 23 20 6f ce 1 OCX is.-# o
cf20: 6e 65 20 70 72 6f 67 72 61 6d 2c 20 6f 63 78 32 ne program, ocx2
cf30: 20 28 64 6f 63 73 2f 73 6c 69 63 65 73 2e 6d 64 (docs/slices.md
cf40: 29 2e 20 54 68 65 20 74 77 6f 20 62 75 69 6c 74 ). The two built
cf50: 20 66 72 6f 6d 20 73 63 72 69 70 74 73 2f 20 61 from scripts/ a
cf60: 72 65 20 74 68 65 20 6d 61 69 6e 74 61 69 6e 65 re the maintaine
cf70: 72 27 73 20 61 6e 64 20 72 75 6e 20 66 72 6f 6d r's and run from
cf80: 0a 2b 23 20 6f 6e 65 20 70 72 6f 67 72 61 6d 2c .+# one program,
cf90: 20 6f 63 78 32 20 28 64 6f 63 73 2f 73 6c 69 63 ocx2 (docs/slic
cfa0: 65 73 2e 6d 64 29 2e 20 54 68 65 20 66 6f 75 72 es.md). The four
cfb0: 20 62 75 69 6c 74 20 66 72 6f 6d 20 73 63 72 69 built from scri
cfc0: 70 74 73 2f 20 61 72 65 20 74 68 65 20 6d 61 69 pts/ are the mai
cfd0: 6e 74 61 69 6e 65 72 27 73 20 61 6e 64 20 72 75 ntainer's and ru
cfe0: 6e 20 66 72 6f 6d 0a 20 23 20 61 20 63 68 65 63 n from. # a chec
cff0: 6b 6f 75 74 27 73 20 62 69 6e 2f 2e 0a 20 74 61 kout's bin/.. ta
d000: 72 67 65 74 73 3a 0a 20 20 20 6f 63 78 32 3a 0a rgets:. ocx2:.
d010: 20 20 20 20 20 6d 61 69 6e 3a 20 74 6f 6f 6c 73 main: tools
d020: 2f 6f 63 78 32 2e 63 72 0a 20 20 20 73 74 61 72 /ocx2.cr. star
d030: 74 2d 66 6f 73 73 69 6c 3a 0a 20 20 20 20 20 6d t-fossil:. m
d040: 61 69 6e 3a 20 73 63 72 69 70 74 73 2f 73 74 61 ain: scripts/sta
d050: 72 74 2d 66 6f 73 73 69 6c 2e 63 72 0a 20 20 20 rt-fossil.cr.
d060: 73 79 6e 63 2d 66 6f 73 73 69 6c 3a 0a 20 20 20 sync-fossil:.
d070: 20 20 6d 61 69 6e 3a 20 73 63 72 69 70 74 73 2f main: scripts/
d080: 73 79 6e 63 2d 66 6f 73 73 69 6c 2e 63 72 0a 2b sync-fossil.cr.+
d090: 20 20 73 74 61 72 74 2d 6f 63 78 3a 0a 2b 20 20 start-ocx:.+
d0a0: 20 20 6d 61 69 6e 3a 20 73 63 72 69 70 74 73 2f main: scripts/
d0b0: 73 74 61 72 74 2d 6f 63 78 2e 63 72 0a 2b 20 20 start-ocx.cr.+
d0c0: 72 65 63 65 69 70 74 73 3a 0a 2b 20 20 20 20 6d receipts:.+ m
d0d0: 61 69 6e 3a 20 73 63 72 69 70 74 73 2f 72 65 63 ain: scripts/rec
d0e0: 65 69 70 74 73 2e 63 72 0a 0a 00 00 00 00 00 00 eipts.cr........
d0f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d1a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d1b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d1c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d1d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d1e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d1f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d200: 73 63 72 69 70 74 73 2d 66 6f 72 2d 72 65 76 69 scripts-for-revi
d210: 65 77 2d 32 2f 52 45 41 44 4d 45 2e 74 78 74 00 ew-2/README.txt.
d220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d260: 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30 ....0000644.0000
d270: 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 000.0000000.0000
d280: 30 30 30 31 32 35 31 00 31 35 32 37 30 35 32 33 0001251.15270523
d290: 31 32 32 00 30 31 35 31 36 31 00 20 30 00 00 00 122.015161. 0...
d2a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d2b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d2c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d2d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d2e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d2f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d300: 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00 .ustar .root...
d310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d320: 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00 .........root...
d330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d3a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d3b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d3c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d3d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d3e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d3f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d400: 53 65 63 6f 6e 64 20 72 65 76 69 65 77 20 63 6f Second review co
d410: 70 79 2c 20 32 30 32 36 2d 31 30 2d 32 39 20 7e py, 2026-10-29 ~
d420: 30 31 3a 35 30 20 55 54 43 2c 20 61 66 74 65 72 01:50 UTC, after
d430: 20 53 6c 6f 77 70 65 6e 27 73 20 73 6f 75 72 63 Slowpen's sourc
d440: 65 20 72 65 76 69 65 77 2e 20 53 75 70 65 72 73 e review. Supers
d450: 65 64 65 73 20 73 63 72 69 70 74 73 2d 66 6f 72 edes scripts-for
d460: 2d 72 65 76 69 65 77 2e 74 61 72 2e 20 4e 6f 74 -review.tar. Not
d470: 20 63 6f 6d 6d 69 74 74 65 64 2e 0a 73 74 61 72 committed..star
d480: 74 2d 6f 63 78 2e 63 72 3a 20 63 6f 6e 6e 65 63 t-ocx.cr: connec
d490: 74 20 61 6e 64 20 69 64 65 6e 74 69 74 79 20 77 t and identity w
d4a0: 72 69 74 65 20 62 6f 75 6e 64 65 64 20 61 74 20 rite bounded at
d4b0: 32 20 73 20 28 53 4f 5f 53 4e 44 54 49 4d 45 4f 2 s (SO_SNDTIMEO
d4c0: 29 3b 20 74 68 65 20 72 65 63 65 69 70 74 20 72 ); the receipt r
d4d0: 6f 75 74 65 20 72 65 61 64 20 61 73 20 73 72 63 oute read as src
d4e0: 2f 6f 63 78 2f 63 67 69 2e 63 72 20 72 65 61 64 /ocx/cgi.cr read
d4f0: 73 20 61 20 70 61 74 68 2c 20 73 6f 20 2f 72 65 s a path, so /re
d500: 63 65 69 70 74 2f 20 61 6e 64 20 2f 72 65 63 65 ceipt/ and /rece
d510: 69 70 74 2f 78 20 72 65 61 63 68 20 74 68 65 20 ipt/x reach the
d520: 73 65 72 76 69 63 65 3b 20 2d 2d 63 61 70 2d 64 service; --cap-d
d530: 72 6f 70 20 41 4c 4c 20 69 6e 20 74 68 65 20 43 rop ALL in the C
d540: 47 49 20 73 61 6e 64 62 6f 78 2e 0a 72 65 63 65 GI sandbox..rece
d550: 69 70 74 73 2e 63 72 3a 20 53 51 4c 69 74 65 20 ipts.cr: SQLite
d560: 62 75 73 79 20 74 69 6d 65 6f 75 74 20 30 20 61 busy timeout 0 a
d570: 6e 64 20 6c 6f 63 6b 20 77 61 69 74 73 20 69 6e nd lock waits in
d580: 20 43 72 79 73 74 61 6c 20 28 73 6c 65 65 70 20 Crystal (sleep
d590: 35 30 20 6d 73 2c 20 61 74 20 6d 6f 73 74 20 32 50 ms, at most 2
d5a0: 20 73 29 2c 20 73 6f 20 61 20 6c 6f 63 6b 65 64 s), so a locked
d5b0: 20 72 65 70 6f 73 69 74 6f 72 79 20 73 74 61 6c repository stal
d5c0: 6c 73 20 6e 6f 20 6f 74 68 65 72 20 72 65 71 75 ls no other requ
d5d0: 65 73 74 3b 20 74 68 65 20 77 68 6f 6c 65 20 63 est; the whole c
d5e0: 6f 6e 6e 65 63 74 69 6f 6e 20 62 6f 75 6e 64 65 onnection bounde
d5f0: 64 20 61 74 20 35 20 73 2e 0a 72 65 63 6f 72 64 d at 5 s..record
d600: 2e 74 78 74 3a 20 74 68 65 20 73 63 72 61 74 63 .txt: the scratc
d610: 68 20 72 75 6e 20 77 69 74 68 20 74 68 65 20 66 h run with the f
d620: 6f 75 72 20 72 65 76 69 65 77 20 74 65 73 74 73 our review tests
d630: 20 28 61 6c 69 61 73 65 73 2c 20 61 20 6c 6f 63 (aliases, a loc
d640: 6b 20 6f 6e 20 6f 6e 65 20 72 65 70 6f 73 69 74 k on one reposit
d650: 6f 72 79 20 77 68 69 6c 65 20 61 6e 6f 74 68 65 ory while anothe
d660: 72 20 69 73 20 61 73 6b 65 64 2c 20 74 68 65 20 r is asked, the
d670: 43 47 49 20 73 61 6e 64 62 6f 78 20 66 72 6f 6d CGI sandbox from
d680: 20 69 6e 73 69 64 65 2c 20 61 20 6c 69 73 74 65 inside, a liste
d690: 6e 65 72 20 74 68 61 74 20 6e 65 76 65 72 20 61 ner that never a
d6a0: 63 63 65 70 74 73 29 2e 0a 00 00 00 00 00 00 00 ccepts).........
d6b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d6c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d6d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d6e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d6f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d720: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d730: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d740: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d750: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d760: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d790: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d7a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d7b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d7c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d7d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d7e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d7f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d870: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d890: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d8a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d8b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d8c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d8d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d8e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d8f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d910: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d920: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d9a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d9b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d9c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d9d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d9e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d9f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
da00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
da10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
da20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
da30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
da40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
da50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
da60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
da70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
da80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
da90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
daa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
dab0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
dac0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
dad0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
dae0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
daf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
db00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
db10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
db20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
db30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
db40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
db50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
db60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
db70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
db80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
db90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
dba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
dbb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
dbc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
dbd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
dbe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
dbf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................