From f7e550b1f484111302d7634d3a313d8667c58907 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 25 Oct 2022 13:21:28 +1000 Subject: [PATCH 001/136] landscape: sketch migration scripts --- pkg/grid/src/state/kiln.ts | 1 + pkg/landscape/sur/migrate.hoon | 11 ++++++++++ pkg/landscape/ted/migrate.hoon | 39 ++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 pkg/landscape/sur/migrate.hoon create mode 100644 pkg/landscape/ted/migrate.hoon diff --git a/pkg/grid/src/state/kiln.ts b/pkg/grid/src/state/kiln.ts index 06589f759..68fd3570c 100644 --- a/pkg/grid/src/state/kiln.ts +++ b/pkg/grid/src/state/kiln.ts @@ -36,6 +36,7 @@ const useKilnState = create((set, get) => ({ return; } const vats = await api.scry(getVats); + console.log(vats); set({ vats, loaded: true }); }, fetchLag: async () => { diff --git a/pkg/landscape/sur/migrate.hoon b/pkg/landscape/sur/migrate.hoon new file mode 100644 index 000000000..0cda39621 --- /dev/null +++ b/pkg/landscape/sur/migrate.hoon @@ -0,0 +1,11 @@ +/- met=metadata-store, gra=graph-store +|% +++ chat + |% + +$ import + [writers=(set ship) =association:met =update-log:gra =graph:gra] + +$ flag (pair ship term) + +$ imports (map flag import) + -- +-- + diff --git a/pkg/landscape/ted/migrate.hoon b/pkg/landscape/ted/migrate.hoon new file mode 100644 index 000000000..ba6821802 --- /dev/null +++ b/pkg/landscape/ted/migrate.hoon @@ -0,0 +1,39 @@ +:: Migrate scripts +/- spider +/- gra=graph-store +/- met=metadata-store +/- grp=group-store +/- i=migrate +/- *group +/+ strandio +=, strand=strand:spider +^- thread:spider +|= arg=vase +=/ m (strand ,vase) +^- form:m +;< =bowl:spider bind:m get-bowl:strandio +;< [%2 =groups] bind:m + (scry:strandio ,[%2 =groups] /gx/group-store/export/noun) +;< [%6 =network:gra] bind:m + (scry:strandio ,[%6 =network:gra] /gx/graph-store/export/noun) +;< =associations:met bind:m + (scry:strandio ,associations:met /gx/metadata-store/associations/noun) +=/ =imports:chat:i + %- ~(gas by *imports:chat:i) + %+ murn ~(tap by graphs.network) + |= [=flag:chat:i graph=graph:gra mar=(unit mark)] + ?. =(mar `%graph-validator-chat) :: XX: correct detection? + ~ + ?~ assoc=(~(get by associations) [%graph flag]) + ~& missing-assoc/flag + ~ + ?~ group=(~(get by groups) group.u.assoc) + ~& missing-group/[flag group.u.assoc] + ~ + =/ writers=(set ship) (~(get ju tags.u.group) %graph flag %writers) + ?~ log=(~(get by update-logs.network) flag) + ~& missing-log/flag :: XX: doesn't need to fail, but suspect case + ~ + `[flag writers u.assoc u.log graph] +;< ~ bind:m (poke-our:strandio %chat %graph-imports !>(imports)) +(pure:m *vase) From 5e89835c5d0242bbd734e67989562e0a8b48a3d9 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 25 Oct 2022 13:49:25 +1000 Subject: [PATCH 002/136] landscape: migration works for chat/groups --- pkg/landscape/sur/migrate.hoon | 8 +++++++- pkg/landscape/ted/migrate.hoon | 12 +++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/pkg/landscape/sur/migrate.hoon b/pkg/landscape/sur/migrate.hoon index 0cda39621..6825ff1a7 100644 --- a/pkg/landscape/sur/migrate.hoon +++ b/pkg/landscape/sur/migrate.hoon @@ -1,10 +1,16 @@ /- met=metadata-store, gra=graph-store +/- *group |% ++$ flag (pair ship term) ++ chat |% +$ import [writers=(set ship) =association:met =update-log:gra =graph:gra] - +$ flag (pair ship term) + +$ imports (map flag import) + -- +++ groups + |% + +$ import [=association:met =group] +$ imports (map flag import) -- -- diff --git a/pkg/landscape/ted/migrate.hoon b/pkg/landscape/ted/migrate.hoon index ba6821802..f417867b0 100644 --- a/pkg/landscape/ted/migrate.hoon +++ b/pkg/landscape/ted/migrate.hoon @@ -18,10 +18,20 @@ (scry:strandio ,[%6 =network:gra] /gx/graph-store/export/noun) ;< =associations:met bind:m (scry:strandio ,associations:met /gx/metadata-store/associations/noun) +=/ =imports:groups:i + %- ~(gas by *imports:groups:i) + %+ murn ~(tap by groups) + |= [=flag:i =group] + ^- (unit [_flag import:groups:i]) + ?~ assoc=(~(get by associations) [%groups flag]) + ~& missing-group-assoc/flag + ~ + `[flag u.assoc group] +;< ~ bind:m (poke-our:strandio %groups group-import+!>(imports)) =/ =imports:chat:i %- ~(gas by *imports:chat:i) %+ murn ~(tap by graphs.network) - |= [=flag:chat:i graph=graph:gra mar=(unit mark)] + |= [=flag:i graph=graph:gra mar=(unit mark)] ?. =(mar `%graph-validator-chat) :: XX: correct detection? ~ ?~ assoc=(~(get by associations) [%graph flag]) From 53621ecb98e6fad4499da576d5d0c71ed0ab472e Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 25 Oct 2022 15:20:40 +1000 Subject: [PATCH 003/136] landscape: migration scripts for heap & dms --- pkg/landscape/sur/migrate.hoon | 15 +++++++++++++ pkg/landscape/ted/migrate.hoon | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/pkg/landscape/sur/migrate.hoon b/pkg/landscape/sur/migrate.hoon index 6825ff1a7..69ec3d3f3 100644 --- a/pkg/landscape/sur/migrate.hoon +++ b/pkg/landscape/sur/migrate.hoon @@ -8,6 +8,21 @@ [writers=(set ship) =association:met =update-log:gra =graph:gra] +$ imports (map flag import) -- +:: +++ diary + |% + +$ import + [writers=(set ship) =association:met =update-log:gra =graph:gra] + +$ imports (map flag import) + -- +:: +++ heap + |% + +$ import + [writers=(set ship) =association:met =update-log:gra =graph:gra] + +$ imports (map flag import) + -- + ++ groups |% +$ import [=association:met =group] diff --git a/pkg/landscape/ted/migrate.hoon b/pkg/landscape/ted/migrate.hoon index f417867b0..28ed69851 100644 --- a/pkg/landscape/ted/migrate.hoon +++ b/pkg/landscape/ted/migrate.hoon @@ -46,4 +46,44 @@ ~ `[flag writers u.assoc u.log graph] ;< ~ bind:m (poke-our:strandio %chat %graph-imports !>(imports)) +=/ =imports:diary:i + %- ~(gas by *imports:diary:i) + %+ murn ~(tap by graphs.network) + |= [=flag:i graph=graph:gra mar=(unit mark)] + ?. =(mar `%graph-validator-publish) :: XX: correct detection? + ~ + ?~ assoc=(~(get by associations) [%graph flag]) + ~& missing-assoc/flag + ~ + ?~ group=(~(get by groups) group.u.assoc) + ~& missing-group/[flag group.u.assoc] + ~ + =/ writers=(set ship) (~(get ju tags.u.group) %graph flag %writers) + ?~ log=(~(get by update-logs.network) flag) + ~& missing-log/flag :: XX: doesn't need to fail, but suspect case + ~ + `[flag writers u.assoc u.log graph] +;< ~ bind:m (poke-our:strandio %diary %graph-imports !>(imports)) +=/ =imports:heap:i + %- ~(gas by *imports:heap:i) + %+ murn ~(tap by graphs.network) + |= [=flag:i graph=graph:gra mar=(unit mark)] + ?. =(mar `%graph-validator-link) :: XX: correct detection? + ~ + ?~ assoc=(~(get by associations) [%graph flag]) + ~& missing-assoc/flag + ~ + ?~ group=(~(get by groups) group.u.assoc) + ~& missing-group/[flag group.u.assoc] + ~ + =/ writers=(set ship) (~(get ju tags.u.group) %graph flag %writers) + ?~ log=(~(get by update-logs.network) flag) + ~& missing-log/flag :: XX: doesn't need to fail, but suspect case + ~ + `[flag writers u.assoc u.log graph] +;< ~ bind:m (poke-our:strandio %link %graph-imports !>(imports)) +;< ~ bind:m + ?~ dms=(~(get by graphs.network) [our.bowl %dm-inbox]) + (pure:(strand ,~) ~) + (poke-our:strandio %chat %dm-imports !>(p.u.dms)) (pure:m *vase) From db34d0cfc8b6f4e7fe3cd7862c55f88d45c2f7f2 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 31 Oct 2022 15:41:47 +1000 Subject: [PATCH 004/136] migrate: fix agent name --- pkg/landscape/ted/migrate.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/ted/migrate.hoon b/pkg/landscape/ted/migrate.hoon index 28ed69851..beb3b6671 100644 --- a/pkg/landscape/ted/migrate.hoon +++ b/pkg/landscape/ted/migrate.hoon @@ -81,7 +81,7 @@ ~& missing-log/flag :: XX: doesn't need to fail, but suspect case ~ `[flag writers u.assoc u.log graph] -;< ~ bind:m (poke-our:strandio %link %graph-imports !>(imports)) +;< ~ bind:m (poke-our:strandio %heap %graph-imports !>(imports)) ;< ~ bind:m ?~ dms=(~(get by graphs.network) [our.bowl %dm-inbox]) (pure:(strand ,~) ~) From 0e16d82a46ea569e04326ba497b99f93b7ddc751 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 3 Nov 2022 12:50:51 +1000 Subject: [PATCH 005/136] wip --- .husky/post-checkout | 3 +++ .husky/post-commit | 3 +++ .husky/post-merge | 3 +++ .husky/pre-push | 3 +++ pkg/arvo/app/azimuth.hoon | 2 +- pkg/urbit/noun/manage.c | 6 ++++++ pkg/urbit/vere/io/fore.c | 7 +++++-- 7 files changed, 24 insertions(+), 3 deletions(-) create mode 100755 .husky/post-checkout create mode 100755 .husky/post-commit create mode 100755 .husky/post-merge create mode 100755 .husky/pre-push diff --git a/.husky/post-checkout b/.husky/post-checkout new file mode 100755 index 000000000..cab40f264 --- /dev/null +++ b/.husky/post-checkout @@ -0,0 +1,3 @@ +#!/bin/sh +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-checkout.\n"; exit 2; } +git lfs post-checkout "$@" diff --git a/.husky/post-commit b/.husky/post-commit new file mode 100755 index 000000000..9443f4161 --- /dev/null +++ b/.husky/post-commit @@ -0,0 +1,3 @@ +#!/bin/sh +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-commit.\n"; exit 2; } +git lfs post-commit "$@" diff --git a/.husky/post-merge b/.husky/post-merge new file mode 100755 index 000000000..828b70891 --- /dev/null +++ b/.husky/post-merge @@ -0,0 +1,3 @@ +#!/bin/sh +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-merge.\n"; exit 2; } +git lfs post-merge "$@" diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 000000000..81a9cc639 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,3 @@ +#!/bin/sh +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/pre-push.\n"; exit 2; } +git lfs pre-push "$@" diff --git a/pkg/arvo/app/azimuth.hoon b/pkg/arvo/app/azimuth.hoon index 9b445efb1..9088043fd 100644 --- a/pkg/arvo/app/azimuth.hoon +++ b/pkg/arvo/app/azimuth.hoon @@ -4,7 +4,7 @@ naive, dice, default-agent, - verb, +/ verb, dbug :: To update, run from dojo: :: -azimuth-snap-state %default 'version-0' diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index c0659f42b..e1dbd5dad 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -433,6 +433,7 @@ u3m_signal(u3_noun sig_l) u3_noun u3m_file(c3_c* pas_c) { + u3l_log("Loading file: %s\r\n", pas_c); struct stat buf_b; c3_i fid_i = c3_open(pas_c, O_RDONLY, 0644); c3_w fln_w, red_w; @@ -443,6 +444,8 @@ u3m_file(c3_c* pas_c) return u3m_bail(c3__fail); } fln_w = buf_b.st_size; + //u3l_log("file size: %i\r\n", fln_w); + pad_y = c3_malloc(buf_b.st_size); red_w = read(fid_i, pad_y, fln_w); @@ -456,6 +459,9 @@ u3m_file(c3_c* pas_c) u3_noun pad = u3i_bytes(fln_w, (c3_y *)pad_y); c3_free(pad_y); + //u3l_log("size of noun\r\n"); + //u3m_p("size", u3dc("met", 3, u3k(pad))); + return pad; } } diff --git a/pkg/urbit/vere/io/fore.c b/pkg/urbit/vere/io/fore.c index 7354a8225..ac3240d6f 100644 --- a/pkg/urbit/vere/io/fore.c +++ b/pkg/urbit/vere/io/fore.c @@ -78,9 +78,12 @@ _fore_inject(u3_auto* car_u, c3_c* pax_c) static void _fore_import(u3_auto* car_u, c3_c* pax_c) { - u3_noun arc = u3ke_cue(u3m_file(pax_c)); - u3_noun imp = u3dt("cat", 3, u3i_string("#import_"), arc); + u3_noun fil = u3m_file(pax_c); + //u3m_p("imp file size", u3dc("met", 3, fil)); + u3_noun imp = u3dt("cat", 3, u3i_string("#import_"), fil); u3_noun siz = u3r_met(3, imp); + u3m_p("imp cue size", siz); + u3_noun dat = u3nt(u3_nul, siz, imp); u3_noun req = u3nt(c3n, From 02549975334bd001c4e232bce68052ea17288e76 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Fri, 4 Nov 2022 16:04:22 -0500 Subject: [PATCH 006/136] Groups/Garden: Add S3 region to s3 settings --- pkg/landscape/app/s3-store.hoon | 33 +++++++++++++++++++--- pkg/landscape/gen/s3-store/set-region.hoon | 10 +++++++ pkg/landscape/lib/s3-json.hoon | 3 ++ pkg/landscape/sur/s3-0.hoon | 27 ++++++++++++++++++ pkg/landscape/sur/s3.hoon | 7 +++++ 5 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 pkg/landscape/gen/s3-store/set-region.hoon create mode 100644 pkg/landscape/sur/s3-0.hoon diff --git a/pkg/landscape/app/s3-store.hoon b/pkg/landscape/app/s3-store.hoon index 061a35cf1..cee795389 100644 --- a/pkg/landscape/app/s3-store.hoon +++ b/pkg/landscape/app/s3-store.hoon @@ -9,12 +9,14 @@ +$ card card:agent:gall +$ versioned-state $% state-zero + state-one == :: -+$ state-zero [%0 =credentials =configuration] ++$ state-zero [%0 =credentials:zero:past =configuration:zero:past] ++$ state-one [%1 =credentials =configuration] -- :: -=| state-zero +=| state-one =* state - :: %- agent:dbug @@ -28,8 +30,28 @@ ++ on-init on-init:def ++ on-save !>(state) ++ on-load - |= old-vase=vase - [~ this(state !<(state-zero old-vase))] + |= =vase + =/ old !<(versioned-state vase) + |^ + ?- -.old + %1 `this(state old) + %0 `this(state (state-0-to-1 old)) + == + ++ state-0-to-1 + |= zer=state-zero + ^- state-one + :* %1 + credentials.zer + (configuration-0-to-1 configuration.zer) + == + ++ configuration-0-to-1 + |= conf=configuration:zero:past + ^- ^configuration + :* buckets.conf + current-bucket.conf + '' + == + -- :: ++ on-poke ~/ %s3-poke @@ -56,6 +78,9 @@ :: %set-secret-access-key state(secret-access-key.credentials secret-access-key.act) + :: + %set-region + state(region.configuration region.act) :: %set-current-bucket %_ state diff --git a/pkg/landscape/gen/s3-store/set-region.hoon b/pkg/landscape/gen/s3-store/set-region.hoon new file mode 100644 index 000000000..d87b09f93 --- /dev/null +++ b/pkg/landscape/gen/s3-store/set-region.hoon @@ -0,0 +1,10 @@ +:: s3-store|set-current-bucket: set current bucket for S3 +:: +/- *s3 +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[region=@t ~] ~] + == +:- %s3-action +^- action +[%set-region region] diff --git a/pkg/landscape/lib/s3-json.hoon b/pkg/landscape/lib/s3-json.hoon index e4a531daa..04cf848b9 100644 --- a/pkg/landscape/lib/s3-json.hoon +++ b/pkg/landscape/lib/s3-json.hoon @@ -10,6 +10,7 @@ :~ [%set-endpoint so:dejs] [%set-access-key-id so:dejs] [%set-secret-access-key so:dejs] + [%set-region so:dejs] [%add-bucket so:dejs] [%remove-bucket so:dejs] [%set-current-bucket so:dejs] @@ -25,6 +26,7 @@ :~ ?- -.upd %set-current-bucket [%'setCurrentBucket' s+bucket.upd] %add-bucket [%'addBucket' s+bucket.upd] + %set-region [%'setRegion' s+region.upd] %remove-bucket [%'removeBucket' s+bucket.upd] %set-endpoint [%'setEndpoint' s+endpoint.upd] %set-access-key-id [%'setAccessKeyId' s+access-key-id.upd] @@ -44,6 +46,7 @@ %- pairs:enjs :~ [%buckets a+(turn ~(tap in buckets.configuration.upd) |=(a=@t s+a))] [%'currentBucket' s+current-bucket.configuration.upd] + [%'region' s+region.configuration.upd] == == == diff --git a/pkg/landscape/sur/s3-0.hoon b/pkg/landscape/sur/s3-0.hoon new file mode 100644 index 000000000..4f0ca04cb --- /dev/null +++ b/pkg/landscape/sur/s3-0.hoon @@ -0,0 +1,27 @@ +|% ++$ credentials + $: endpoint=@t + access-key-id=@t + secret-access-key=@t + == +:: ++$ configuration + $: buckets=(set @t) + current-bucket=@t + == +:: ++$ action + $% [%set-endpoint endpoint=@t] + [%set-access-key-id access-key-id=@t] + [%set-secret-access-key secret-access-key=@t] + [%add-bucket bucket=@t] + [%remove-bucket bucket=@t] + [%set-current-bucket bucket=@t] + == +:: ++$ update + $% [%credentials =credentials] + [%configuration =configuration] + action + == +-- diff --git a/pkg/landscape/sur/s3.hoon b/pkg/landscape/sur/s3.hoon index 4f0ca04cb..a79ca68ce 100644 --- a/pkg/landscape/sur/s3.hoon +++ b/pkg/landscape/sur/s3.hoon @@ -1,4 +1,9 @@ +/- zer=s3-0 |% +++ past + |% + ++ zero zer + -- +$ credentials $: endpoint=@t access-key-id=@t @@ -8,6 +13,7 @@ +$ configuration $: buckets=(set @t) current-bucket=@t + region=@t == :: +$ action @@ -17,6 +23,7 @@ [%add-bucket bucket=@t] [%remove-bucket bucket=@t] [%set-current-bucket bucket=@t] + [%set-region region=@t] == :: +$ update From 11be7d59d2cc3fe8c7d3bcb986a23305b087cb5b Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 7 Nov 2022 12:02:15 +1000 Subject: [PATCH 007/136] landscape: rework migration slightly --- pkg/landscape/sur/migrate.hoon | 18 +----- pkg/landscape/ted/migrate.hoon | 113 +++++++++++++++++---------------- 2 files changed, 60 insertions(+), 71 deletions(-) diff --git a/pkg/landscape/sur/migrate.hoon b/pkg/landscape/sur/migrate.hoon index 69ec3d3f3..2f8fca0e5 100644 --- a/pkg/landscape/sur/migrate.hoon +++ b/pkg/landscape/sur/migrate.hoon @@ -2,30 +2,16 @@ /- *group |% +$ flag (pair ship term) -++ chat +++ graph |% +$ import [writers=(set ship) =association:met =update-log:gra =graph:gra] +$ imports (map flag import) -- :: -++ diary - |% - +$ import - [writers=(set ship) =association:met =update-log:gra =graph:gra] - +$ imports (map flag import) - -- -:: -++ heap - |% - +$ import - [writers=(set ship) =association:met =update-log:gra =graph:gra] - +$ imports (map flag import) - -- - ++ groups |% - +$ import [=association:met =group] + +$ import [=association:met chans=(map flag association:met) roles=(set flag) =group] +$ imports (map flag import) -- -- diff --git a/pkg/landscape/ted/migrate.hoon b/pkg/landscape/ted/migrate.hoon index beb3b6671..ae10bda70 100644 --- a/pkg/landscape/ted/migrate.hoon +++ b/pkg/landscape/ted/migrate.hoon @@ -7,6 +7,30 @@ /- *group /+ strandio =, strand=strand:spider +|% +++ import-for-mark + |= [=groups =associations:met =network:gra] + |= =mark + ^- imports:graph:i + %- ~(gas by *imports:graph:i) + %+ murn ~(tap by graphs.network) + |= [=flag:i graph=graph:gra mar=(unit ^mark)] + ?. =(mar `mark) :: XX: correct detection? + ~ + ?~ assoc=(~(get by associations) [%graph flag]) + ~& missing-assoc/flag^mark + ~ + ?~ group=(~(get by groups) group.u.assoc) + ~& missing-group/[flag group.u.assoc] + ~ + =/ writers=(set ship) + (~(get ju tags.u.group) %graph flag %writers) + ?~ log=(~(get by update-logs.network) flag) + ~& missing-log/flag :: XX: doesn't need to fail, but suspect case + ~ + `[flag writers u.assoc u.log graph] +:: +-- ^- thread:spider |= arg=vase =/ m (strand ,vase) @@ -18,6 +42,13 @@ (scry:strandio ,[%6 =network:gra] /gx/graph-store/export/noun) ;< =associations:met bind:m (scry:strandio ,associations:met /gx/metadata-store/associations/noun) +=/ import (import-for-mark groups associations network) +=/ chats=imports:graph:i + (import %graph-validator-chat) +=/ diarys=imports:graph:i + (import %graph-validator-publish) +=/ links=imports:graph:i + (import %graph-validator-link) =/ =imports:groups:i %- ~(gas by *imports:groups:i) %+ murn ~(tap by groups) @@ -26,62 +57,34 @@ ?~ assoc=(~(get by associations) [%groups flag]) ~& missing-group-assoc/flag ~ - `[flag u.assoc group] + =/ chans=(map flag:i association:met) + %- ~(gas by *(map flag:i association:met)) + %+ murn ~(tap by associations) + |= [res=md-resource:met ass=association:met] + ^- (unit [flag:i association:met]) + ?. =(group.ass flag) + ~ + `[resource.res ass] + =/ roles=(set flag:i) + %- ~(gas in *(set flag:i)) + %+ murn ~(tap by chans) + |= [=flag:i =association:met] + ^- (unit flag:i) + ?^ link=(~(get by links) flag) + ?: =(writers.u.link ~) ~ + `flag + ?^ diary=(~(get by diarys) flag) + ?: =(writers.u.diary ~) ~ + `flag + ?^ chat=(~(get by chats) flag) + ?: =(writers.u.chat ~) ~ + `flag + ~ + `[flag u.assoc chans roles group] ;< ~ bind:m (poke-our:strandio %groups group-import+!>(imports)) -=/ =imports:chat:i - %- ~(gas by *imports:chat:i) - %+ murn ~(tap by graphs.network) - |= [=flag:i graph=graph:gra mar=(unit mark)] - ?. =(mar `%graph-validator-chat) :: XX: correct detection? - ~ - ?~ assoc=(~(get by associations) [%graph flag]) - ~& missing-assoc/flag - ~ - ?~ group=(~(get by groups) group.u.assoc) - ~& missing-group/[flag group.u.assoc] - ~ - =/ writers=(set ship) (~(get ju tags.u.group) %graph flag %writers) - ?~ log=(~(get by update-logs.network) flag) - ~& missing-log/flag :: XX: doesn't need to fail, but suspect case - ~ - `[flag writers u.assoc u.log graph] -;< ~ bind:m (poke-our:strandio %chat %graph-imports !>(imports)) -=/ =imports:diary:i - %- ~(gas by *imports:diary:i) - %+ murn ~(tap by graphs.network) - |= [=flag:i graph=graph:gra mar=(unit mark)] - ?. =(mar `%graph-validator-publish) :: XX: correct detection? - ~ - ?~ assoc=(~(get by associations) [%graph flag]) - ~& missing-assoc/flag - ~ - ?~ group=(~(get by groups) group.u.assoc) - ~& missing-group/[flag group.u.assoc] - ~ - =/ writers=(set ship) (~(get ju tags.u.group) %graph flag %writers) - ?~ log=(~(get by update-logs.network) flag) - ~& missing-log/flag :: XX: doesn't need to fail, but suspect case - ~ - `[flag writers u.assoc u.log graph] -;< ~ bind:m (poke-our:strandio %diary %graph-imports !>(imports)) -=/ =imports:heap:i - %- ~(gas by *imports:heap:i) - %+ murn ~(tap by graphs.network) - |= [=flag:i graph=graph:gra mar=(unit mark)] - ?. =(mar `%graph-validator-link) :: XX: correct detection? - ~ - ?~ assoc=(~(get by associations) [%graph flag]) - ~& missing-assoc/flag - ~ - ?~ group=(~(get by groups) group.u.assoc) - ~& missing-group/[flag group.u.assoc] - ~ - =/ writers=(set ship) (~(get ju tags.u.group) %graph flag %writers) - ?~ log=(~(get by update-logs.network) flag) - ~& missing-log/flag :: XX: doesn't need to fail, but suspect case - ~ - `[flag writers u.assoc u.log graph] -;< ~ bind:m (poke-our:strandio %heap %graph-imports !>(imports)) +;< ~ bind:m (poke-our:strandio %chat graph-imports+!>(chats)) +;< ~ bind:m (poke-our:strandio %diary graph-imports+!>(diarys)) +;< ~ bind:m (poke-our:strandio %heap graph-imports+!>(links)) ;< ~ bind:m ?~ dms=(~(get by graphs.network) [our.bowl %dm-inbox]) (pure:(strand ,~) ~) From bc535a56835cc68f52ea6bd23ca8f50996f5aeab Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 7 Nov 2022 13:35:51 +1000 Subject: [PATCH 008/136] landscape: fix broken migration --- pkg/landscape/sur/migrate.hoon | 2 +- pkg/landscape/ted/migrate.hoon | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/landscape/sur/migrate.hoon b/pkg/landscape/sur/migrate.hoon index 2f8fca0e5..3d5904229 100644 --- a/pkg/landscape/sur/migrate.hoon +++ b/pkg/landscape/sur/migrate.hoon @@ -11,7 +11,7 @@ :: ++ groups |% - +$ import [=association:met chans=(map flag association:met) roles=(set flag) =group] + +$ import [=association:met chans=(map flag =association:met) roles=(set flag) =group] +$ imports (map flag import) -- -- diff --git a/pkg/landscape/ted/migrate.hoon b/pkg/landscape/ted/migrate.hoon index ae10bda70..ab92d379f 100644 --- a/pkg/landscape/ted/migrate.hoon +++ b/pkg/landscape/ted/migrate.hoon @@ -62,8 +62,7 @@ %+ murn ~(tap by associations) |= [res=md-resource:met ass=association:met] ^- (unit [flag:i association:met]) - ?. =(group.ass flag) - ~ + ?. =(group.ass flag) ~ `[resource.res ass] =/ roles=(set flag:i) %- ~(gas in *(set flag:i)) From 97e7a141236417c11edb8255f82aa7eb70eb8e89 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 17 Nov 2022 23:44:12 -0500 Subject: [PATCH 009/136] u3: restages #6062 for release --- pkg/urbit/Makefile | 3 +- pkg/urbit/c/defs.c | 96 +++++++++++++++++ pkg/urbit/compat/mingw/compat.c | 57 ++++++++++ pkg/urbit/compat/mingw/compat.h | 2 + pkg/urbit/include/c/defs.h | 29 ++++++ pkg/urbit/include/vere/vere.h | 5 - pkg/urbit/noun/events.c | 178 ++++++++++---------------------- pkg/urbit/noun/manage.c | 6 +- pkg/urbit/noun/urth.c | 50 +-------- pkg/urbit/vere/io/http.c | 37 ++++--- pkg/urbit/vere/io/term.c | 54 ++-------- pkg/urbit/vere/io/unix.c | 55 +++++----- pkg/urbit/vere/king.c | 82 +++++---------- pkg/urbit/vere/lord.c | 2 +- 14 files changed, 329 insertions(+), 327 deletions(-) create mode 100644 pkg/urbit/c/defs.c diff --git a/pkg/urbit/Makefile b/pkg/urbit/Makefile index 6b934bc79..f86731275 100644 --- a/pkg/urbit/Makefile +++ b/pkg/urbit/Makefile @@ -2,6 +2,7 @@ include config.mk compat_mks := $(foreach dir,$(compat),$(wildcard compat/$(dir)/*.mk)) include $(compat_mks) +c = $(wildcard c/*.c) jets = jets/tree.c $(wildcard jets/*/*.c) noun = $(wildcard noun/*.c) ur = $(wildcard ur/*.c) @@ -13,7 +14,7 @@ bench = $(wildcard bench/*.c) compat := $(foreach dir,$(compat),$(wildcard compat/$(dir)/*.c)) -common = $(jets) $(noun) $(ur) $(vere) $(compat) +common = $(c) $(jets) $(noun) $(ur) $(vere) $(compat) headers = $(shell find include -type f) common_objs = $(shell echo $(common) | sed 's/\.c/.o/g') diff --git a/pkg/urbit/c/defs.c b/pkg/urbit/c/defs.c new file mode 100644 index 000000000..93a1d9b89 --- /dev/null +++ b/pkg/urbit/c/defs.c @@ -0,0 +1,96 @@ +/// @file defs.c + +#include "c/defs.h" + +/* c3_pread(): full positioned read(), up to eof, retrying errors. +*/ +ssize_t +c3_pread(c3_i fid_i, void* buf_v, size_t len_i, off_t off_i) +{ + c3_w max_w = 128; + c3_w try_w = 0; + size_t rem_i = len_i; + ssize_t ret_i; + + do { + if ( (0 > (ret_i = pread(fid_i, buf_v, rem_i, off_i))) + && ( (++try_w == max_w) + || ( (EINTR != errno) + && (EAGAIN != errno) + && (EWOULDBLOCK != errno) ))) + { + return -1; + } + else if ( 0 == ret_i ) { + break; + } + else { + buf_v = (void*)((c3_c*)buf_v + ret_i); + rem_i -= ret_i; + off_i += ret_i; + } + + } + while ( rem_i ); + + return len_i - rem_i; +} + +/* c3_pwrite(): full positioned write(), retrying errors. +*/ +ssize_t +c3_pwrite(c3_i fid_i, const void* buf_v, size_t len_i, off_t off_i) +{ + c3_w max_w = 128; + c3_w try_w = 0; + size_t rem_i = len_i; + ssize_t ret_i; + + do { + if ( (0 > (ret_i = pwrite(fid_i, buf_v, rem_i, off_i))) + && ( (++try_w == max_w) + || ( (EINTR != errno) + && (EAGAIN != errno) + && (EWOULDBLOCK != errno) ))) + { + return -1; + } + else { + buf_v = (void*)((c3_c*)buf_v + ret_i); + rem_i -= ret_i; + off_i += ret_i; + } + } + while ( rem_i ); + + return len_i; +} + +/* c3_write(): full write(), retrying errors. +*/ +ssize_t +c3_write(c3_i fid_i, const void* buf_v, size_t len_i) +{ + c3_w max_w = 128; + c3_w try_w = 0; + size_t rem_i = len_i; + ssize_t ret_i; + + do { + if ( (0 > (ret_i = write(fid_i, buf_v, rem_i))) + && ( (++try_w == max_w) + || ( (EINTR != errno) + && (EAGAIN != errno) + && (EWOULDBLOCK != errno) ))) + { + return -1; + } + else { + buf_v = (void*)((c3_c*)buf_v + ret_i); + rem_i -= ret_i; + } + } + while ( rem_i ); + + return len_i; +} diff --git a/pkg/urbit/compat/mingw/compat.c b/pkg/urbit/compat/mingw/compat.c index ebef5041f..1bd142128 100644 --- a/pkg/urbit/compat/mingw/compat.c +++ b/pkg/urbit/compat/mingw/compat.c @@ -144,6 +144,63 @@ int link(const char *path1, const char *path2) return -1; } +ssize_t pread(int fd, void *buf, size_t count, off_t offset) +{ + DWORD len = 0; + + OVERLAPPED overlapped = {0}; + + overlapped.OffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)0 : (DWORD)((offset >> 32) & 0xFFFFFFFFL); + overlapped.Offset = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)offset : (DWORD)(offset & 0xFFFFFFFFL); + + HANDLE h = (HANDLE)_get_osfhandle(fd); + + if ( INVALID_HANDLE_VALUE == h ) { + errno = EBADF; + return -1; + } + + if ( !ReadFile(h, buf, count, &len, &overlapped) ) { + DWORD err = GetLastError(); + + if ( ERROR_HANDLE_EOF != err ) { + errno = err_win_to_posix(err); + return -1; + } + } + + return (ssize_t)len; +} + +ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) +{ + DWORD len = 0; + + OVERLAPPED overlapped = {0}; + + overlapped.OffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)0 : (DWORD)((offset >> 32) & 0xFFFFFFFFL); + overlapped.Offset = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)offset : (DWORD)(offset & 0xFFFFFFFFL); + + HANDLE h = (HANDLE)_get_osfhandle(fd); + + if ( INVALID_HANDLE_VALUE == h ) { + errno = EBADF; + return -1; + } + + if ( !WriteFile(h, buf, count, &len, &overlapped) ) { + errno = err_win_to_posix(GetLastError()); + return -1; + } + + return (ssize_t)len; +} + + // from msys2 mingw-packages-dev patches // ----------------------------------------------------------------------- diff --git a/pkg/urbit/compat/mingw/compat.h b/pkg/urbit/compat/mingw/compat.h index 56c35e90a..80809f54b 100644 --- a/pkg/urbit/compat/mingw/compat.h +++ b/pkg/urbit/compat/mingw/compat.h @@ -4,6 +4,8 @@ #define mkdir(A, B) mkdir(A) int link(const char *path1, const char *path2); +ssize_t pread(int fd, void *buf, size_t count, off_t offset); +ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); char *realpath(const char *path, char *resolved_path); int fdatasync(int fd); int utimes(const char *path, const struct timeval times[2]); diff --git a/pkg/urbit/include/c/defs.h b/pkg/urbit/include/c/defs.h index b649a8866..a667b85a2 100644 --- a/pkg/urbit/include/c/defs.h +++ b/pkg/urbit/include/c/defs.h @@ -1,6 +1,11 @@ #ifndef C3_DEFS_H #define C3_DEFS_H +#include "c/portable.h" +#include "c/types.h" + +#include + /** Loobeans - inverse booleans to match nock. **/ # define c3y 0 @@ -157,4 +162,28 @@ # define c3_fopen(a, b) ({ \ fopen(a, b);}) + /** i/o wrappers + *** + *** these handle partial success and retry ephemeral errors + *** up to hardcoded max try count, either reading/writing fully + *** (up to EOF on read) or returning on error. + *** + *** a wrapper for read() is not provided, as file cursor position + *** is undefined on error. use pread() or loop yourself. + **/ + /* c3_pread(): full positioned read(), up to eof, retrying errors. + */ + ssize_t + c3_pread(c3_i fid_i, void* buf_v, size_t len_i, off_t off_i); + + /* c3_pwrite(): full positioned write(), retrying errors. + */ + ssize_t + c3_pwrite(c3_i fid_i, const void* buf_v, size_t len_i, off_t off_i); + + /* c3_write(): full write(), retrying errors. + */ + ssize_t + c3_write(c3_i fid_i, const void* buf_v, size_t len_i); + #endif /* ifndef C3_DEFS_H */ diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index d02771343..f02bf4a46 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -1461,11 +1461,6 @@ void u3_daemon_init(); - /* u3_write_fd(): retry interrupts, continue partial writes, assert errors. - */ - void - u3_write_fd(c3_i fid_i, const void* buf_v, size_t len_i); - c3_w u3_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index ceec1bd41..c1d53eebb 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -354,17 +354,11 @@ _ce_image_open(u3e_image* img_u) static void _ce_patch_write_control(u3_ce_patch* pat_u) { - ssize_t ret_i; - c3_w len_w = sizeof(u3e_control) + - (pat_u->con_u->pgs_w * sizeof(u3e_line)); + c3_w len_w = sizeof(u3e_control) + + (pat_u->con_u->pgs_w * sizeof(u3e_line)); - if ( len_w != (ret_i = write(pat_u->ctl_i, pat_u->con_u, len_w)) ) { - if ( 0 < ret_i ) { - fprintf(stderr, "loom: patch ctl partial write: %zu\r\n", (size_t)ret_i); - } - else { - fprintf(stderr, "loom: patch ctl write: %s\r\n", strerror(errno)); - } + if ( 0 > c3_pwrite(pat_u->ctl_i, pat_u->con_u, len_w, 0) ) { + fprintf(stderr, "loom: patch write: %s\r\n", strerror(errno)); c3_assert(0); } } @@ -388,8 +382,9 @@ _ce_patch_read_control(u3_ce_patch* pat_u) } pat_u->con_u = c3_malloc(len_w); - if ( (len_w != read(pat_u->ctl_i, pat_u->con_u, len_w)) || - (len_w != sizeof(u3e_control) + + + if ( (len_w != c3_pread(pat_u->ctl_i, pat_u->con_u, len_w, 0)) + || (len_w != sizeof(u3e_control) + (pat_u->con_u->pgs_w * sizeof(u3e_line))) ) { c3_free(pat_u->con_u); @@ -450,8 +445,10 @@ _ce_patch_delete(void) static c3_o _ce_patch_verify(u3_ce_patch* pat_u) { - ssize_t ret_i; - c3_w i_w; + c3_w i_w, pag_w, mug_w; + c3_w mem_w[pag_wiz_i]; + size_t off_i, siz_i = pag_siz_i; + ssize_t ret_i; if ( u3e_version != pat_u->con_u->ver_y ) { fprintf(stderr, "loom: patch version mismatch: have %u, need %u\r\n", @@ -461,15 +458,11 @@ _ce_patch_verify(u3_ce_patch* pat_u) } for ( i_w = 0; i_w < pat_u->con_u->pgs_w; i_w++ ) { - c3_w pag_w = pat_u->con_u->mem_u[i_w].pag_w; - c3_w mug_w = pat_u->con_u->mem_u[i_w].mug_w; - c3_w mem_w[1 << u3a_page]; + pag_w = pat_u->con_u->mem_u[i_w].pag_w; + mug_w = pat_u->con_u->mem_u[i_w].mug_w; + off_i = i_w << (u3a_page + 2); - if ( -1 == lseek(pat_u->mem_i, (i_w << (u3a_page + 2)), SEEK_SET) ) { - fprintf(stderr, "loom: patch seek: %s\r\n", strerror(errno)); - return c3n; - } - if ( pag_siz_i != (ret_i = read(pat_u->mem_i, mem_w, pag_siz_i)) ) { + if ( siz_i != (ret_i = c3_pread(pat_u->mem_i, mem_w, siz_i, off_i)) ) { if ( 0 < ret_i ) { fprintf(stderr, "loom: patch partial read: %zu\r\n", (size_t)ret_i); } @@ -562,21 +555,10 @@ _ce_patch_write_page(u3_ce_patch* pat_u, c3_w pgc_w, c3_w* mem_w) { - ssize_t ret_i; + size_t off_i = pgc_w << (u3a_page + 2); - if ( -1 == lseek(pat_u->mem_i, pgc_w * pag_siz_i, SEEK_SET) ) { - fprintf(stderr, "loom: patch page seek: %s\r\n", strerror(errno)); - c3_assert(0); - } - - if ( pag_siz_i != (ret_i = write(pat_u->mem_i, mem_w, pag_siz_i)) ) { - if ( 0 < ret_i ) { - fprintf(stderr, "loom: patch page partial write: %zu\r\n", - (size_t)ret_i); - } - else { - fprintf(stderr, "loom: patch page write: %s\r\n", strerror(errno)); - } + if ( 0 > c3_pwrite(pat_u->mem_i, mem_w, pag_siz_i, off_i) ) { + fprintf(stderr, "loom: patch write: %s\r\n", strerror(errno)); c3_assert(0); } } @@ -753,31 +735,20 @@ _ce_image_resize(u3e_image* img_u, c3_w pgs_w) static void _ce_patch_apply(u3_ce_patch* pat_u) { - ssize_t ret_i; - c3_w i_w; + c3_w i_w, pag_w, off_w, mem_w[pag_wiz_i]; + c3_i fid_i; + size_t rof_i, wof_i, siz_i = pag_siz_i; + ssize_t ret_i; // resize images // _ce_image_resize(&u3P.nor_u, pat_u->con_u->nor_w); _ce_image_resize(&u3P.sou_u, pat_u->con_u->sou_w); - // seek to begining of patch and images - // - if ( (-1 == lseek(pat_u->mem_i, 0, SEEK_SET)) - || (-1 == lseek(u3P.nor_u.fid_i, 0, SEEK_SET)) - || (-1 == lseek(u3P.sou_u.fid_i, 0, SEEK_SET)) ) - { - fprintf(stderr, "loom: patch apply seek 0: %s\r\n", strerror(errno)); - c3_assert(0); - } - // write patch pages into the appropriate image // for ( i_w = 0; i_w < pat_u->con_u->pgs_w; i_w++ ) { - c3_w pag_w = pat_u->con_u->mem_u[i_w].pag_w; - c3_w mem_w[pag_wiz_i]; - c3_i fid_i; - c3_w off_w; + pag_w = pat_u->con_u->mem_u[i_w].pag_w; if ( pag_w < pat_u->con_u->nor_w ) { fid_i = u3P.nor_u.fid_i; @@ -788,7 +759,10 @@ _ce_patch_apply(u3_ce_patch* pat_u) off_w = (u3P.pag_w - (pag_w + 1)); } - if ( pag_siz_i != (ret_i = read(pat_u->mem_i, mem_w, pag_siz_i)) ) { + rof_i = (size_t)i_w << (u3a_page + 2); + wof_i = (size_t)off_w << (u3a_page + 2); + + if ( siz_i != (ret_i = c3_pread(pat_u->mem_i, mem_w, siz_i, rof_i))) { if ( 0 < ret_i ) { fprintf(stderr, "loom: patch apply partial read: %zu\r\n", (size_t)ret_i); @@ -798,21 +772,10 @@ _ce_patch_apply(u3_ce_patch* pat_u) } c3_assert(0); } - else { - if ( -1 == lseek(fid_i, (off_w << (u3a_page + 2)), SEEK_SET) ) { - fprintf(stderr, "loom: patch apply seek: %s\r\n", strerror(errno)); - c3_assert(0); - } - if ( pag_siz_i != (ret_i = write(fid_i, mem_w, pag_siz_i)) ) { - if ( 0 < ret_i ) { - fprintf(stderr, "loom: patch apply partial write: %zu\r\n", - (size_t)ret_i); - } - else { - fprintf(stderr, "loom: patch apply write: %s\r\n", strerror(errno)); - } - c3_assert(0); - } + + if ( 0 > c3_pwrite(fid_i, mem_w, siz_i, wof_i) ) { + fprintf(stderr, "loom: patch apply write: %s\r\n", strerror(errno)); + c3_assert(0); } #if 0 u3l_log("apply: %d, %x\n", pag_w, u3r_mug_words(mem_w, pag_wiz_i)); @@ -827,22 +790,18 @@ _ce_image_blit(u3e_image* img_u, c3_w* ptr_w, c3_ws stp_ws) { + c3_w i_w; + size_t off_i, siz_i = pag_siz_i; + ssize_t ret_i; + if ( 0 == img_u->pgs_w ) { return; } - ssize_t ret_i; - c3_w i_w; - c3_w siz_w = pag_siz_i; - - if ( -1 == lseek(img_u->fid_i, 0, SEEK_SET) ) { - fprintf(stderr, "loom: image (%s) blit seek 0: %s\r\n", - img_u->nam_c, strerror(errno)); - c3_assert(0); - } - for ( i_w = 0; i_w < img_u->pgs_w; i_w++ ) { - if ( siz_w != (ret_i = read(img_u->fid_i, ptr_w, siz_w)) ) { + off_i = (size_t)i_w << (u3a_page + 2); + + if ( siz_i != (ret_i = c3_pread(img_u->fid_i, ptr_w, siz_i, off_i)) ) { if ( 0 < ret_i ) { fprintf(stderr, "loom: image (%s) blit partial read: %zu\r\n", img_u->nam_c, (size_t)ret_i); @@ -854,7 +813,7 @@ _ce_image_blit(u3e_image* img_u, c3_assert(0); } - if ( 0 != mprotect(ptr_w, siz_w, PROT_READ) ) { + if ( 0 != mprotect(ptr_w, siz_i, PROT_READ) ) { fprintf(stderr, "loom: live mprotect: %s\r\n", strerror(errno)); c3_assert(0); } @@ -876,19 +835,15 @@ _ce_image_fine(u3e_image* img_u, c3_w* ptr_w, c3_ws stp_ws) { - ssize_t ret_i; - c3_w i_w; - c3_w buf_w[pag_wiz_i]; - - if ( -1 == lseek(img_u->fid_i, 0, SEEK_SET) ) { - fprintf(stderr, "loom: image fine seek 0: %s\r\n", strerror(errno)); - c3_assert(0); - } + c3_w i_w, mem_w, fil_w; + c3_w buf_w[pag_wiz_i]; + size_t off_i, siz_i = pag_siz_i; + ssize_t ret_i; for ( i_w=0; i_w < img_u->pgs_w; i_w++ ) { - c3_w mem_w, fil_w; + off_i = (size_t)i_w << (u3a_page + 2); - if ( pag_siz_i != (ret_i = read(img_u->fid_i, buf_w, pag_siz_i)) ) { + if ( siz_i != (ret_i = c3_pread(img_u->fid_i, buf_w, siz_i, off_i)) ) { if ( 0 < ret_i ) { fprintf(stderr, "loom: image (%s) fine partial read: %zu\r\n", img_u->nam_c, (size_t)ret_i); @@ -899,6 +854,7 @@ _ce_image_fine(u3e_image* img_u, } c3_assert(0); } + mem_w = u3r_mug_words(ptr_w, pag_wiz_i); fil_w = u3r_mug_words(buf_w, pag_wiz_i); @@ -924,31 +880,21 @@ _ce_image_fine(u3e_image* img_u, static c3_o _ce_image_copy(u3e_image* fom_u, u3e_image* tou_u) { + c3_w i_w; + c3_w mem_w[pag_wiz_i]; + size_t off_i, siz_i = pag_siz_i; ssize_t ret_i; - c3_w i_w; // resize images // _ce_image_resize(tou_u, fom_u->pgs_w); - // seek to begining of patch and images - // - if ( (-1 == lseek(fom_u->fid_i, 0, SEEK_SET)) - || (-1 == lseek(tou_u->fid_i, 0, SEEK_SET)) ) - { - fprintf(stderr, "loom: image (%s) copy seek: %s\r\n", - fom_u->nam_c, - strerror(errno)); - return c3n; - } - // copy pages into destination image // for ( i_w = 0; i_w < fom_u->pgs_w; i_w++ ) { - c3_w mem_w[pag_wiz_i]; - c3_w off_w = i_w; + off_i = (size_t)i_w << (u3a_page + 2); - if ( pag_siz_i != (ret_i = read(fom_u->fid_i, mem_w, pag_siz_i)) ) { + if ( siz_i != (ret_i = c3_pread(fom_u->fid_i, mem_w, siz_i, off_i)) ) { if ( 0 < ret_i ) { fprintf(stderr, "loom: image (%s) copy partial read: %zu\r\n", fom_u->nam_c, (size_t)ret_i); @@ -959,23 +905,11 @@ _ce_image_copy(u3e_image* fom_u, u3e_image* tou_u) } return c3n; } - else { - if ( -1 == lseek(tou_u->fid_i, (off_w << (u3a_page + 2)), SEEK_SET) ) { - fprintf(stderr, "loom: image (%s) copy seek: %s\r\n", - tou_u->nam_c, strerror(errno)); - return c3n; - } - if ( pag_siz_i != (ret_i = write(tou_u->fid_i, mem_w, pag_siz_i)) ) { - if ( 0 < ret_i ) { - fprintf(stderr, "loom: image (%s) copy partial write: %zu\r\n", - tou_u->nam_c, (size_t)ret_i); - } - else { - fprintf(stderr, "loom: image (%s) copy write: %s\r\n", - tou_u->nam_c, strerror(errno)); - } - return c3n; - } + + if ( 0 > c3_pwrite(tou_u->fid_i, mem_w, siz_i, off_i) ) { + fprintf(stderr, "loom: image (%s) copy write: %s\r\n", + tou_u->nam_c, strerror(errno)); + return c3n; } } diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index aa33b0328..12d4d0fe9 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -435,7 +435,7 @@ u3m_file(c3_c* pas_c) { struct stat buf_b; c3_i fid_i = c3_open(pas_c, O_RDONLY, 0644); - c3_w fln_w, red_w; + c3_w fln_w; c3_y* pad_y; if ( (fid_i < 0) || (fstat(fid_i, &buf_b) < 0) ) { @@ -445,10 +445,10 @@ u3m_file(c3_c* pas_c) fln_w = buf_b.st_size; pad_y = c3_malloc(buf_b.st_size); - red_w = read(fid_i, pad_y, fln_w); + ssize_t red_i = c3_pread(fid_i, pad_y, fln_w, 0); close(fid_i); - if ( fln_w != red_w ) { + if ( red_i != fln_w ) { c3_free(pad_y); return u3m_bail(c3__fail); } diff --git a/pkg/urbit/noun/urth.c b/pkg/urbit/noun/urth.c index 30ab44817..0b8f02294 100644 --- a/pkg/urbit/noun/urth.c +++ b/pkg/urbit/noun/urth.c @@ -564,56 +564,14 @@ _cu_rock_save(c3_c* dir_c, c3_d eve_d, c3_d len_d, c3_y* byt_y) // write jam-buffer into [fid_i] // - // XX deduplicate with _write() wrapper in term.c - // - { - ssize_t ret_i; - - while ( len_d > 0 ) { - c3_w lop_w = 0; - // retry interrupt/async errors - // - do { - // abort pathological retry loop - // - if ( 100 == ++lop_w ) { - fprintf(stderr, "rock: write loop: %s\r\n", strerror(errno)); - close(fid_i); - // XX unlink file? - // - return c3n; - } - - ret_i = write(fid_i, byt_y, len_d); - } - while ( (ret_i < 0) - && ( (errno == EINTR) - || (errno == EAGAIN) - || (errno == EWOULDBLOCK) )); - - // assert on true errors - // - // NB: can't call u3l_log here or we would re-enter _write() - // - if ( ret_i < 0 ) { - fprintf(stderr, "rock: write failed %s\r\n", strerror(errno)); - close(fid_i); - // XX unlink file? - // - return c3n; - } - // continue partial writes - // - else { - len_d -= ret_i; - byt_y += ret_i; - } - } + ssize_t rit_i = c3_pwrite(fid_i, byt_y, len_d, 0); + if ( rit_i < 0 ) { + fprintf(stderr, "rock: write failed: %s\r\n", strerror(errno)); } close(fid_i); - return c3y; + return rit_i < 0 ? c3n : c3y; } /* u3u_cram(): globably deduplicate memory, and write a rock to disk. diff --git a/pkg/urbit/vere/io/http.c b/pkg/urbit/vere/io/http.c index fa7d778f2..a564c46ce 100644 --- a/pkg/urbit/vere/io/http.c +++ b/pkg/urbit/vere/io/http.c @@ -1677,24 +1677,30 @@ _http_init_tls(uv_buf_t key_u, uv_buf_t cer_u) static void _http_write_ports_file(u3_httd* htd_u, c3_c *pax_c) { - c3_c* nam_c = ".http.ports"; - c3_w len_w = 1 + strlen(pax_c) + 1 + strlen(nam_c); - - c3_c* paf_c = c3_malloc(len_w); - snprintf(paf_c, len_w, "%s/%s", pax_c, nam_c); - - c3_i por_i = c3_open(paf_c, O_WRONLY | O_CREAT | O_TRUNC, 0666); - c3_free(paf_c); - u3_http* htp_u = htd_u->htp_u; u3_pier* pir_u = htd_u->car_u.pir_u; + size_t off_i = 0; + c3_i por_i; + + { + c3_c* nam_c = ".http.ports"; + c3_w len_w = 1 + strlen(pax_c) + 1 + strlen(nam_c); + c3_c* paf_c = c3_malloc(len_w); + snprintf(paf_c, len_w, "%s/%s", pax_c, nam_c); + por_i = c3_open(paf_c, O_WRONLY | O_CREAT | O_TRUNC, 0666); + c3_free(paf_c); + } - c3_c temp[32]; while ( 0 != htp_u ) { if ( 0 < htp_u->por_s ) { - u3_write_fd(por_i, temp, snprintf(temp, 32, "%u %s %s\n", htp_u->por_s, - (c3y == htp_u->sec) ? "secure" : "insecure", - (c3y == htp_u->lop) ? "loopback" : "public")); + c3_c tmp_c[32]; + c3_i len_i = snprintf(tmp_c, 32, "%u %s %s\n", + htp_u->por_s, + (c3y == htp_u->sec) ? "secure" : "insecure", + (c3y == htp_u->lop) ? "loopback" : "public"); + c3_assert( 0 < len_i); + c3_assert( c3_pwrite(por_i, tmp_c, len_i, off_i) == len_i ); + off_i += len_i; } htp_u = htp_u->nex_u; @@ -1710,13 +1716,12 @@ static void _http_release_ports_file(c3_c *pax_c) { c3_c* nam_c = ".http.ports"; - c3_w len_w = 1 + strlen(pax_c) + 1 + strlen(nam_c); + c3_w len_w = 1 + strlen(pax_c) + 1 + strlen(nam_c); c3_c* paf_c = c3_malloc(len_w); c3_i wit_i; wit_i = snprintf(paf_c, len_w, "%s/%s", pax_c, nam_c); - c3_assert(wit_i > 0); - c3_assert(len_w == (c3_w)wit_i + 1); + c3_assert( len_w == (c3_w)wit_i + 1 ); c3_unlink(paf_c); c3_free(paf_c); diff --git a/pkg/urbit/vere/io/term.c b/pkg/urbit/vere/io/term.c index 9994fcfcb..d13bbb9ae 100644 --- a/pkg/urbit/vere/io/term.c +++ b/pkg/urbit/vere/io/term.c @@ -17,48 +17,6 @@ static void _term_read_cb(uv_stream_t* tcp_u, ssize_t siz_i, const uv_buf_t* buf_u); -/* u3_write_fd(): retry interrupts, continue partial writes, assert errors. -*/ -void -u3_write_fd(c3_i fid_i, const void* buf_v, size_t len_i) -{ - ssize_t ret_i; - - while ( len_i > 0 ) { - c3_w lop_w = 0; - // retry interrupt/async errors - // - do { - // abort pathological retry loop - // - if ( 100 == ++lop_w ) { - fprintf(stderr, "term: write loop: %s\r\n", strerror(errno)); - return; - } - ret_i = write(fid_i, buf_v, len_i); - } - while ( (ret_i < 0) - && ( (errno == EINTR) - || (errno == EAGAIN) - || (errno == EWOULDBLOCK) )); - - // assert on true errors - // - // NB: can't call u3l_log here or we would re-enter u3_write_fd() - // - if ( ret_i < 0 ) { - fprintf(stderr, "term: write failed %s\r\n", strerror(errno)); - c3_assert(0); - } - // continue partial writes - // - else { - len_i -= ret_i; - buf_v += ret_i; - } - } -} - /* _term_msc_out_host(): unix microseconds from current host time. */ static c3_d @@ -255,7 +213,7 @@ u3_term_log_exit(void) if ( c3n == uty_u->sto_f(uty_u) ) { c3_assert(!"exit-tcsetattr"); } - u3_write_fd(uty_u->fid_i, "\r\n", 2); + c3_assert(c3_write(uty_u->fid_i, "\r\n", 2) == 2); } } @@ -864,7 +822,7 @@ _term_spin_step(u3_utty* uty_u) c3_w i_w; for ( i_w = bac_w; i_w < sol_w; i_w++ ) { - if ( lef_u.len != write(fid_i, lef_u.base, lef_u.len) ) { + if ( c3_write(fid_i, lef_u.base, lef_u.len) < 0 ) { return; } } @@ -874,7 +832,7 @@ _term_spin_step(u3_utty* uty_u) { c3_w len_w = cur_c - buf_c; - if ( len_w != write(fid_i, buf_c, len_w) ) { + if ( c3_write(fid_i, buf_c, len_w) < 0 ) { return; } } @@ -882,7 +840,7 @@ _term_spin_step(u3_utty* uty_u) // Cursor stays on spinner. // while ( sol_w-- ) { - if ( lef_u.len != write(fid_i, lef_u.base, lef_u.len) ) { + if ( c3_write(fid_i, lef_u.base, lef_u.len) < 0 ) { return; } } @@ -1382,10 +1340,10 @@ u3_term_io_hija(void) if ( c3y != uty_u->hij_f(uty_u) ) { c3_assert(!"hija-tcsetattr"); } - u3_write_fd(uty_u->fid_i, "\r", 1); + c3_assert(c3_write(uty_u->fid_i, "\r", 1) == 1); { uv_buf_t* buf_u = &uty_u->ufo_u.out.el_u; - u3_write_fd(uty_u->fid_i, buf_u->base, buf_u->len); + c3_assert(c3_write(uty_u->fid_i, buf_u->base, buf_u->len) == buf_u->len); } } } diff --git a/pkg/urbit/vere/io/unix.c b/pkg/urbit/vere/io/unix.c index f9e175b5d..ba0145d71 100644 --- a/pkg/urbit/vere/io/unix.c +++ b/pkg/urbit/vere/io/unix.c @@ -342,11 +342,11 @@ u3_unix_save(c3_c* pax_c, u3_atom pad) pad_y = c3_malloc(fln_w); u3r_bytes(0, fln_w, pad_y, pad); u3z(pad); - rit_w = write(fid_i, pad_y, fln_w); + ssize_t rit_i = c3_pwrite(fid_i, pad_y, fln_w, 0); close(fid_i); c3_free(pad_y); - if ( rit_w != fln_w ) { + if ( rit_i < 0 ) { u3l_log("%s: %s\n", ful_c, strerror(errno)); c3_free(ful_c); u3m_bail(c3__fail); @@ -427,7 +427,7 @@ static c3_w _unix_write_file_hard(c3_c* pax_c, u3_noun mim) { c3_i fid_i = c3_open(pax_c, O_WRONLY | O_CREAT | O_TRUNC, 0666); - c3_w len_w, rit_w, siz_w, mug_w = 0; + c3_w len_w, siz_w, mug_w = 0; c3_y* dat_y; u3_noun dat = u3t(u3t(mim)); @@ -446,11 +446,10 @@ _unix_write_file_hard(c3_c* pax_c, u3_noun mim) u3r_bytes(0, len_w, dat_y, dat); u3z(mim); - rit_w = write(fid_i, dat_y, siz_w); + ssize_t rit_i = c3_pwrite(fid_i, dat_y, siz_w, 0); - if ( rit_w != siz_w ) { - u3l_log("error writing %s: %s\r\n", - pax_c, strerror(errno)); + if ( rit_i < 0 ) { + u3l_log("error writing %s: %s\r\n", pax_c, strerror(errno)); mug_w = 0; } else { @@ -470,7 +469,7 @@ _unix_write_file_soft(u3_ufil* fil_u, u3_noun mim) { struct stat buf_u; c3_i fid_i = c3_open(fil_u->pax_c, O_RDONLY, 0644); - c3_ws len_ws, red_ws; + c3_ws len_ws; c3_w old_w; c3_y* old_y; @@ -489,21 +488,21 @@ _unix_write_file_soft(u3_ufil* fil_u, u3_noun mim) len_ws = buf_u.st_size; old_y = c3_malloc(len_ws); - red_ws = read(fid_i, old_y, len_ws); + ssize_t red_i = c3_pread(fid_i, old_y, len_ws, 0); if ( close(fid_i) < 0 ) { u3l_log("error closing file (soft) %s: %s\r\n", fil_u->pax_c, strerror(errno)); } - if ( len_ws != red_ws ) { - if ( red_ws < 0 ) { + if ( red_i != len_ws ) { + if ( red_i < 0 ) { u3l_log("error reading file (soft) %s: %s\r\n", - fil_u->pax_c, strerror(errno)); + fil_u->pax_c, strerror(-red_i)); } else { - u3l_log("wrong # of bytes read in file %s: %d %d\r\n", - fil_u->pax_c, len_ws, red_ws); + u3l_log("wrong # of bytes read in file %s: %u %zu\r\n", + fil_u->pax_c, len_ws, red_i); } c3_free(old_y); u3z(mim); @@ -893,7 +892,7 @@ _unix_update_file(u3_unix* unx_u, u3_ufil* fil_u) struct stat buf_u; c3_i fid_i = c3_open(fil_u->pax_c, O_RDONLY, 0644); - c3_ws len_ws, red_ws; + c3_ws len_ws; c3_y* dat_y; if ( fid_i < 0 || fstat(fid_i, &buf_u) < 0 ) { @@ -910,21 +909,21 @@ _unix_update_file(u3_unix* unx_u, u3_ufil* fil_u) len_ws = buf_u.st_size; dat_y = c3_malloc(len_ws); - red_ws = read(fid_i, dat_y, len_ws); + ssize_t red_i = c3_pread(fid_i, dat_y, len_ws, 0); if ( close(fid_i) < 0 ) { u3l_log("error closing file %s: %s\r\n", fil_u->pax_c, strerror(errno)); } - if ( len_ws != red_ws ) { - if ( red_ws < 0 ) { + if ( red_i != len_ws ) { + if ( red_i < 0 ) { u3l_log("error reading file %s: %s\r\n", - fil_u->pax_c, strerror(errno)); + fil_u->pax_c, strerror(-red_i)); } else { - u3l_log("wrong # of bytes read in file %s: %d %d\r\n", - fil_u->pax_c, len_ws, red_ws); + u3l_log("wrong # of bytes read in file %s: %u %zu\r\n", + fil_u->pax_c, len_ws, red_i); } c3_free(dat_y); return u3_nul; @@ -1159,7 +1158,7 @@ _unix_initial_update_file(c3_c* pax_c, c3_c* bas_c) { struct stat buf_u; c3_i fid_i = c3_open(pax_c, O_RDONLY, 0644); - c3_ws len_ws, red_ws; + c3_ws len_ws; c3_y* dat_y; if ( fid_i < 0 || fstat(fid_i, &buf_u) < 0 ) { @@ -1176,21 +1175,21 @@ _unix_initial_update_file(c3_c* pax_c, c3_c* bas_c) len_ws = buf_u.st_size; dat_y = c3_malloc(len_ws); - red_ws = read(fid_i, dat_y, len_ws); + ssize_t red_i = c3_pread(fid_i, dat_y, len_ws, 0); if ( close(fid_i) < 0 ) { u3l_log("error closing initial file %s: %s\r\n", pax_c, strerror(errno)); } - if ( len_ws != red_ws ) { - if ( red_ws < 0 ) { + if ( red_i != len_ws ) { + if ( red_i < 0 ) { u3l_log("error reading initial file %s: %s\r\n", - pax_c, strerror(errno)); + pax_c, strerror(-red_i)); } else { - u3l_log("wrong # of bytes read in initial file %s: %d %d\r\n", - pax_c, len_ws, red_ws); + u3l_log("wrong # of bytes read in initial file %s: %u %zu\r\n", + pax_c, len_ws, red_i); } c3_free(dat_y); return u3_nul; diff --git a/pkg/urbit/vere/king.c b/pkg/urbit/vere/king.c index 44a8cd502..b18d7b441 100644 --- a/pkg/urbit/vere/king.c +++ b/pkg/urbit/vere/king.c @@ -307,7 +307,7 @@ _king_get_pace(void) { struct stat buf_u; c3_c* pat_c; - c3_w red_w, len_w; + c3_w len_w; c3_i ret_i, fid_i; ret_i = asprintf(&pat_c, "%s/.bin/pace", u3_Host.dir_c); @@ -324,10 +324,10 @@ _king_get_pace(void) len_w = buf_u.st_size; pat_c = c3_malloc(len_w + 1); - red_w = read(fid_i, pat_c, len_w); + ssize_t red_i = c3_pread(fid_i, pat_c, len_w, 0); close(fid_i); - if ( len_w != red_w ) { + if ( red_i != len_w ) { c3_free(pat_c); u3l_log("unable to read pace file, " "falling back to default (\"live\")\n"); @@ -1038,9 +1038,6 @@ _king_make_pace(c3_c* pac_c) return 0; } -static c3_i -_king_write_raw(c3_i fid_i, c3_y* buf_y, size_t len_i); - /* _king_init_pace(): save pace file if not present */ static c3_i @@ -1064,8 +1061,11 @@ _king_init_pace(c3_c* pac_c) } } - if ( _king_write_raw(fid_i, (c3_y*)pac_c, strlen(pac_c)) ) { - u3l_log("dock: init pace (%s): write %s\n", pac_c, strerror(errno)); + size_t len_i = strlen(pac_c); + ssize_t wit_i = c3_pwrite(fid_i, pac_c, len_i, 0); + if ( wit_i != len_i ) { + u3l_log("dock: init pace (%s) write failed: %s\n", + pac_c, strerror(errno)); close(fid_i); c3_free(bin_c); return -1; @@ -1239,62 +1239,24 @@ _king_do_upgrade(c3_c* pac_c, c3_c* ver_c) // XX print restart instructions } -/* _king_read_raw: read (up to) [len_i] from [fid_i] to [buf_y] -*/ -static ssize_t -_king_read_raw(c3_i fid_i, c3_y* buf_y, size_t len_i) -{ - ssize_t ret_i; - - do { - ret_i = read(fid_i, buf_y, len_i); - } - while ( (ret_i < 0) && (errno == EINTR) ); - - return ret_i; -} - -/* _king_read_raw: write [len_i] from [buf_y] to [fid_i]. -*/ -static c3_i -_king_write_raw(c3_i fid_i, c3_y* buf_y, size_t len_i) -{ - ssize_t ret_i; - - while ( len_i ) { - - do { - ret_i = write(fid_i, buf_y, len_i); - } - while ( (ret_i < 0) && (errno == EINTR) ); - - if ( ret_i < 0 ) { - return -1; - } - else { - len_i -= ret_i; - buf_y += ret_i; - } - } - - return 0; -} - static c3_i _king_copy_raw(c3_i src_i, c3_i dst_i, c3_y* buf_y, size_t pag_i) { - ssize_t red_i; + size_t off_i = 0; + ssize_t ret_i; do { - if ( 0 > (red_i = _king_read_raw(src_i, buf_y, pag_i)) ) { - return -1; + if ( 0 > (ret_i = c3_pread(src_i, buf_y, pag_i, off_i)) ) { + return ret_i; } - if ( _king_write_raw(dst_i, buf_y, (size_t)red_i) ) { - return -1; + if ( 0 > (ret_i = c3_pwrite(dst_i, buf_y, (size_t)ret_i, off_i)) ) { + return ret_i; } + + off_i += (size_t)ret_i; } - while ( red_i ); + while ( ret_i ); return 0; } @@ -1337,6 +1299,8 @@ _king_copy_file(c3_c* src_c, c3_c* dst_c) goto done1; } + // XX O_TRUNC? + // if ( -1 == (dst_i = open(dst_c, O_RDWR | O_CREAT, 0755)) ) { err_i = errno; ret_i = -1; @@ -1403,8 +1367,12 @@ _king_copy_file(c3_c* src_c, c3_c* dst_c) { size_t pag_i = 1 << 14;; c3_y* buf_y = c3_malloc(pag_i); - ret_i = _king_copy_raw(src_i, dst_i, buf_y, pag_i); - err_i = errno; + + if ( 0 > (ret_i = _king_copy_raw(src_i, dst_i, buf_y, pag_i)) ) { + err_i = errno; + ret_i = -1; + } + c3_free(buf_y); } diff --git a/pkg/urbit/vere/lord.c b/pkg/urbit/vere/lord.c index 205190848..d51945530 100644 --- a/pkg/urbit/vere/lord.c +++ b/pkg/urbit/vere/lord.c @@ -1051,7 +1051,7 @@ _lord_on_serf_err_cb(uv_stream_t* pyp_u, // serf used to write to 2 directly // this can't be any worse than that // - u3_write_fd(2, buf_u->base, siz_i); + c3_assert(c3_write(STDERR_FILENO, buf_u->base, siz_i) == siz_i); } else { uv_read_stop(pyp_u); From 622f86e714c06c3a70f6f629f86d3402f819a126 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 2 Nov 2022 17:02:14 -0400 Subject: [PATCH 010/136] u3: misc cleanup of snapshot system corrects comments removes dead code makes page and offset calculations more consistent improves error messages --- pkg/urbit/noun/events.c | 94 +++++++++++++---------------------------- 1 file changed, 29 insertions(+), 65 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index c1d53eebb..1f350b8fb 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -58,16 +58,14 @@ //! - update atomicity is suspect: patch application must either //! completely succeed or leave on-disk segments intact. unapplied //! patches can be discarded (triggering event replay), but once -//! patch application begins it must succeed (can fail if disk is full). +//! patch application begins it must succeed. //! may require integration into the overall signal-handling regime. -//! - any errors are handled with assertions; failed/partial writes are not -//! retried. +//! - many errors are handled with assertions. //! //! ### enhancements //! //! - use platform specific page fault mechanism (mach rpc, userfaultfd, &c). //! - implement demand paging / heuristic page-out. -//! - add a guard page in the middle of the loom to reactively handle stack overflow. //! - parallelism //! @@ -80,10 +78,10 @@ static u3p(c3_w) gar_pag_p; //! Urbit page size in 4-byte words. -static const size_t pag_wiz_i = 1 << u3a_page; +static const size_t pag_wiz_i = (size_t)1 << u3a_page; //! Urbit page size in bytes. -static const size_t pag_siz_i = sizeof(c3_w) * pag_wiz_i; +static const size_t pag_siz_i = (size_t)1 << (u3a_page + 2); #ifdef U3_SNAPSHOT_VALIDATION /* Image check. @@ -100,7 +98,7 @@ static c3_w _ce_check_page(c3_w pag_w) { c3_w* mem_w = u3_Loom + (pag_w << u3a_page); - c3_w mug_w = u3r_mug_words(mem_w, (1 << u3a_page)); + c3_w mug_w = u3r_mug_words(mem_w, pag_wiz_i); return mug_w; } @@ -118,8 +116,8 @@ u3e_check(c3_c* cap_c) u3m_water(&nwr_w, &swu_w); - nor_w = (nwr_w + ((1 << u3a_page) - 1)) >> u3a_page; - sou_w = (swu_w + ((1 << u3a_page) - 1)) >> u3a_page; + nor_w = (nwr_w + (pag_wiz_i - 1)) >> u3a_page; + sou_w = (swu_w + (pag_wiz_i - 1)) >> u3a_page; } /* Count dirty pages. @@ -145,45 +143,6 @@ u3e_check(c3_c* cap_c) u3l_log("%s: sum %x (%x, %x)\r\n", cap_c, sum_w, nor_w, sou_w); } } - -/* _ce_maplloc(): crude off-loom allocator. -*/ -static void* -_ce_maplloc(c3_w len_w) -{ - void* map_v; - - map_v = mmap(0, - len_w, - (PROT_READ | PROT_WRITE), - (MAP_ANON | MAP_PRIVATE), - -1, 0); - - if ( -1 == (c3_ps)map_v ) { - c3_assert(0); - } - else { - c3_w* map_w = map_v; - - map_w[0] = len_w; - - return map_w + 1; - } -} - -/* _ce_mapfree(): crude off-loom allocator. -*/ -static void -_ce_mapfree(void* map_v) -{ - c3_w* map_w = map_v; - c3_i res_i; - - map_w -= 1; - res_i = munmap(map_w, map_w[0]); - - c3_assert(0 == res_i); -} #endif #ifdef U3_GUARD_PAGE @@ -427,7 +386,7 @@ _ce_patch_delete(void) { c3_c ful_c[8193]; - snprintf(ful_c, 8192, "%s/.urb/chk/control.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s/.urb/chk/control.bin", u3P.dir_c); if ( unlink(ful_c) ) { fprintf(stderr, "loom: failed to delete control.bin: %s\r\n", strerror(errno)); @@ -460,7 +419,7 @@ _ce_patch_verify(u3_ce_patch* pat_u) for ( i_w = 0; i_w < pat_u->con_u->pgs_w; i_w++ ) { pag_w = pat_u->con_u->mem_u[i_w].pag_w; mug_w = pat_u->con_u->mem_u[i_w].mug_w; - off_i = i_w << (u3a_page + 2); + off_i = (size_t)i_w << (u3a_page + 2); if ( siz_i != (ret_i = c3_pread(pat_u->mem_i, mem_w, siz_i, off_i)) ) { if ( 0 < ret_i ) { @@ -555,7 +514,7 @@ _ce_patch_write_page(u3_ce_patch* pat_u, c3_w pgc_w, c3_w* mem_w) { - size_t off_i = pgc_w << (u3a_page + 2); + size_t off_i = (size_t)pgc_w << (u3a_page + 2); if ( 0 > c3_pwrite(pat_u->mem_i, mem_w, pag_siz_i, off_i) ) { fprintf(stderr, "loom: patch write: %s\r\n", strerror(errno)); @@ -707,8 +666,7 @@ _ce_image_sync(u3e_image* img_u) { if ( -1 == c3_sync(img_u->fid_i) ) { fprintf(stderr, "loom: image (%s) sync failed: %s\r\n", - img_u->nam_c, - strerror(errno)); + img_u->nam_c, strerror(errno)); c3_assert(!"loom: image sync"); } } @@ -718,11 +676,19 @@ _ce_image_sync(u3e_image* img_u) static void _ce_image_resize(u3e_image* img_u, c3_w pgs_w) { + off_t off_i = (off_t)pgs_w << (u3a_page + 2); + if ( img_u->pgs_w > pgs_w ) { - if ( ftruncate(img_u->fid_i, pgs_w << (u3a_page + 2)) ) { + if ( (off_i >> (u3a_page + 2)) != pgs_w ) { + fprintf(stderr, "loom: image (%s) truncate: " + "offset overflow (%" PRId64 ") for page %u\r\n", + img_u->nam_c, (c3_ds)off_i, pgs_w); + c3_assert(0); + } + + if ( ftruncate(img_u->fid_i, off_i) ) { fprintf(stderr, "loom: image (%s) truncate: %s\r\n", - img_u->nam_c, - strerror(errno)); + img_u->nam_c, strerror(errno)); c3_assert(0); } } @@ -794,10 +760,6 @@ _ce_image_blit(u3e_image* img_u, size_t off_i, siz_i = pag_siz_i; ssize_t ret_i; - if ( 0 == img_u->pgs_w ) { - return; - } - for ( i_w = 0; i_w < img_u->pgs_w; i_w++ ) { off_i = (size_t)i_w << (u3a_page + 2); @@ -832,8 +794,8 @@ _ce_image_blit(u3e_image* img_u, */ static void _ce_image_fine(u3e_image* img_u, - c3_w* ptr_w, - c3_ws stp_ws) + c3_w* ptr_w, + c3_ws stp_ws) { c3_w i_w, mem_w, fil_w; c3_w buf_w[pag_wiz_i]; @@ -875,7 +837,9 @@ _ce_image_fine(u3e_image* img_u, } #endif -/* _ce_image_copy(): +/* _ce_image_copy(): copy all of [fom_u] to [tou_u] +** +** XX use reflinks a la _king_copy_file()? */ static c3_o _ce_image_copy(u3e_image* fom_u, u3e_image* tou_u) @@ -916,14 +880,14 @@ _ce_image_copy(u3e_image* fom_u, u3e_image* tou_u) return c3y; } -/* _ce_backup(); +/* _ce_backup(); copy snapshot to .urb/bhk (if it doesn't exist yet). */ static void _ce_backup(void) { u3e_image nop_u = { .nam_c = "north", .pgs_w = 0 }; u3e_image sop_u = { .nam_c = "south", .pgs_w = 0 }; - c3_i mod_i = O_RDWR | O_CREAT; + c3_i mod_i = O_RDWR | O_CREAT; // XX O_TRUNC ? c3_c ful_c[8193]; snprintf(ful_c, 8192, "%s/.urb/bhk", u3P.dir_c); From 0959bb60f315488c783b32ca0576560041504bc6 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 2 Nov 2022 21:51:51 -0400 Subject: [PATCH 011/136] u3: coalesces memory protection when loading a snapshot --- pkg/urbit/noun/events.c | 98 ++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 25 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 1f350b8fb..cc1b325f4 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -749,43 +749,96 @@ _ce_patch_apply(u3_ce_patch* pat_u) } } -/* _ce_image_blit(): apply image to memory. +/* _ce_loom_blit_north(): apply pages, in order, from the bottom of memory. */ static void -_ce_image_blit(u3e_image* img_u, - c3_w* ptr_w, - c3_ws stp_ws) +_ce_loom_blit_north(c3_i fid_i, c3_w pgs_w) { c3_w i_w; - size_t off_i, siz_i = pag_siz_i; + c3_w* ptr_w; + size_t off_i; ssize_t ret_i; - for ( i_w = 0; i_w < img_u->pgs_w; i_w++ ) { + for ( i_w = 0; i_w < pgs_w; i_w++ ) { off_i = (size_t)i_w << (u3a_page + 2); + ptr_w = u3_Loom + (i_w << u3a_page); - if ( siz_i != (ret_i = c3_pread(img_u->fid_i, ptr_w, siz_i, off_i)) ) { + if ( pag_siz_i != (ret_i = c3_pread(fid_i, ptr_w, pag_siz_i, off_i)) ) { if ( 0 < ret_i ) { - fprintf(stderr, "loom: image (%s) blit partial read: %zu\r\n", - img_u->nam_c, (size_t)ret_i); + fprintf(stderr, "loom: blit north partial read: %zu\r\n", + (size_t)ret_i); } else { - fprintf(stderr, "loom: image (%s) blit read: %s\r\n", - img_u->nam_c, strerror(errno)); + fprintf(stderr, "loom: blit north read: %s\r\n", strerror(errno)); } c3_assert(0); } + } - if ( 0 != mprotect(ptr_w, siz_i, PROT_READ) ) { - fprintf(stderr, "loom: live mprotect: %s\r\n", strerror(errno)); + if ( 0 != mprotect((void*)u3_Loom, + (size_t)pgs_w << (u3a_page + 2), + PROT_READ) ) + { + fprintf(stderr, "loom: protect north: %s\r\n", strerror(errno)); + c3_assert(0); + } + + { + c3_w blk_w = pgs_w >> 5; + c3_w bit_w = pgs_w & 31; + + memset((void*)u3P.dit_w, 0, blk_w << 2); + u3P.dit_w[blk_w] &= 0xffffffff << bit_w; + } +} + +/* _ce_loom_blit_south(): apply pages, reversed, from the top of memory. +*/ +static void +_ce_loom_blit_south(c3_i fid_i, c3_w pgs_w) +{ + c3_w lof_w, i_w; + c3_w* ptr_w; + size_t off_i; + ssize_t ret_i; + + for ( i_w = 0; i_w < pgs_w; i_w++ ) { + off_i = (size_t)i_w << (u3a_page + 2); + lof_w = u3P.pag_w - (i_w + 1); + ptr_w = u3_Loom + (lof_w << u3a_page); + + if ( pag_siz_i != (ret_i = c3_pread(fid_i, ptr_w, pag_siz_i, off_i)) ) { + if ( 0 < ret_i ) { + fprintf(stderr, "loom: blit south partial read: %zu\r\n", + (size_t)ret_i); + } + else { + fprintf(stderr, "loom: blit south read: %s\r\n", strerror(errno)); + } c3_assert(0); } + } - c3_w pag_w = u3a_outa(ptr_w) >> u3a_page; - c3_w blk_w = pag_w >> 5; - c3_w bit_w = pag_w & 31; - u3P.dit_w[blk_w] &= ~(1 << bit_w); + c3_assert ( (u3P.pag_w - pgs_w) == lof_w ); - ptr_w += stp_ws; + if ( 0 != mprotect((void*)(u3_Loom + (lof_w << u3a_page)), + (size_t)pgs_w << (u3a_page + 2), + PROT_READ) ) + { + fprintf(stderr, "loom: protect south: %s\r\n", strerror(errno)); + c3_assert(0); + } + + { + c3_w blk_w = pgs_w >> 5; + c3_w bit_w = pgs_w & 31; + c3_w bas_w = (lof_w + 31) >> 5; + + memset((void*)(u3P.dit_w + bas_w), 0, blk_w << 2); + + // this is safe so long as the south segment never includes all pages + // + u3P.dit_w[bas_w - 1] &= 0xffffffff >> bit_w; } } @@ -1059,13 +1112,8 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) /* Write image files to memory; reinstate protection. */ { - _ce_image_blit(&u3P.nor_u, - u3_Loom, - pag_wiz_i); - - _ce_image_blit(&u3P.sou_u, - (u3_Loom + u3C.wor_i) - pag_wiz_i, - -(ssize_t)pag_wiz_i); + _ce_loom_blit_north(u3P.nor_u.fid_i, u3P.nor_u.pgs_w); + _ce_loom_blit_south(u3P.sou_u.fid_i, u3P.sou_u.pgs_w); u3l_log("boot: protected loom\r\n"); } From 0682cc2864945a246a36634a8cabb45bfcbe4ea3 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 3 Nov 2022 00:30:31 -0400 Subject: [PATCH 012/136] u3: refactors _ce_loom_blit_north(), replacing loop with a single read --- pkg/urbit/noun/events.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index cc1b325f4..e239e4885 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -754,16 +754,11 @@ _ce_patch_apply(u3_ce_patch* pat_u) static void _ce_loom_blit_north(c3_i fid_i, c3_w pgs_w) { - c3_w i_w; - c3_w* ptr_w; - size_t off_i; + size_t len_i = (size_t)pgs_w << (u3a_page + 2); ssize_t ret_i; - for ( i_w = 0; i_w < pgs_w; i_w++ ) { - off_i = (size_t)i_w << (u3a_page + 2); - ptr_w = u3_Loom + (i_w << u3a_page); - - if ( pag_siz_i != (ret_i = c3_pread(fid_i, ptr_w, pag_siz_i, off_i)) ) { + if ( pgs_w ) { + if ( len_i != (ret_i = c3_pread(fid_i, u3_Loom, len_i, 0)) ) { if ( 0 < ret_i ) { fprintf(stderr, "loom: blit north partial read: %zu\r\n", (size_t)ret_i); From a04521585c2f6e1812d14c74e15fcf29245b7d22 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 2 Nov 2022 22:46:12 -0400 Subject: [PATCH 013/136] u3: coalesces memory protection when saving a snapshot --- pkg/urbit/noun/events.c | 133 ++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 66 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index e239e4885..46e2837ac 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -507,21 +507,6 @@ _ce_patch_open(void) return pat_u; } -/* _ce_patch_write_page(): write a page of patch memory. -*/ -static void -_ce_patch_write_page(u3_ce_patch* pat_u, - c3_w pgc_w, - c3_w* mem_w) -{ - size_t off_i = (size_t)pgc_w << (u3a_page + 2); - - if ( 0 > c3_pwrite(pat_u->mem_i, mem_w, pag_siz_i, off_i) ) { - fprintf(stderr, "loom: patch write: %s\r\n", strerror(errno)); - c3_assert(0); - } -} - /* _ce_patch_count_page(): count a page, producing new counter. */ static c3_w @@ -548,25 +533,17 @@ _ce_patch_save_page(u3_ce_patch* pat_u, c3_w bit_w = (pag_w & 31); if ( u3P.dit_w[blk_w] & (1 << bit_w) ) { - c3_w* mem_w = u3_Loom + (pag_w << u3a_page); + c3_w* mem_w = u3_Loom + (pag_w << u3a_page); + size_t off_i = (size_t)pgc_w << (u3a_page + 2); pat_u->con_u->mem_u[pgc_w].pag_w = pag_w; pat_u->con_u->mem_u[pgc_w].mug_w = u3r_mug_words(mem_w, pag_wiz_i); -#if 0 - u3l_log("protect a: page %d\r\n", pag_w); -#endif - _ce_patch_write_page(pat_u, pgc_w, mem_w); - - if ( -1 == mprotect(u3_Loom + (pag_w << u3a_page), - pag_siz_i, - PROT_READ) ) - { - fprintf(stderr, "loom: patch mprotect: %s\r\n", strerror(errno)); + if ( 0 > c3_pwrite(pat_u->mem_i, mem_w, pag_siz_i, off_i) ) { + fprintf(stderr, "loom: patch save: %s\r\n", strerror(errno)); c3_assert(0); } - u3P.dit_w[blk_w] &= ~(1 << bit_w); pgc_w += 1; } return pgc_w; @@ -749,6 +726,62 @@ _ce_patch_apply(u3_ce_patch* pat_u) } } +/* _ce_loom_protect_north(): protect/track pages from the bottom of memory. +*/ +static void +_ce_loom_protect_north(c3_w pgs_w) +{ + if ( pgs_w ) { + if ( 0 != mprotect((void*)u3_Loom, + (size_t)pgs_w << (u3a_page + 2), + PROT_READ) ) + { + fprintf(stderr, "loom: protect north (%u pages): %s\r\n", + pgs_w, strerror(errno)); + c3_assert(0); + } + } + + { + c3_w blk_w = pgs_w >> 5; + c3_w bit_w = pgs_w & 31; + + memset((void*)u3P.dit_w, 0, blk_w << 2); + u3P.dit_w[blk_w] &= 0xffffffff << bit_w; + } +} + +/* _ce_loom_protect_south(): protect/track pages from the top of memory. +*/ +static void +_ce_loom_protect_south(c3_w pgs_w) +{ + c3_w lof_w = u3P.pag_w - pgs_w; + + if ( pgs_w ) { + if ( 0 != mprotect((void*)(u3_Loom + (lof_w << u3a_page)), + (size_t)pgs_w << (u3a_page + 2), + PROT_READ) ) + { + fprintf(stderr, "loom: protect south (%u pages): %s\r\n", + pgs_w, strerror(errno)); + c3_assert(0); + } + } + + { + c3_w blk_w = pgs_w >> 5; + c3_w bit_w = pgs_w & 31; + c3_w bas_w = (lof_w + 31) >> 5; + + memset((void*)(u3P.dit_w + bas_w), 0, blk_w << 2); + + // this is safe so long as the south segment never includes all pages + // + u3P.dit_w[bas_w - 1] &= 0xffffffff >> bit_w; + } +} + /* _ce_loom_blit_north(): apply pages, in order, from the bottom of memory. */ static void @@ -770,21 +803,7 @@ _ce_loom_blit_north(c3_i fid_i, c3_w pgs_w) } } - if ( 0 != mprotect((void*)u3_Loom, - (size_t)pgs_w << (u3a_page + 2), - PROT_READ) ) - { - fprintf(stderr, "loom: protect north: %s\r\n", strerror(errno)); - c3_assert(0); - } - - { - c3_w blk_w = pgs_w >> 5; - c3_w bit_w = pgs_w & 31; - - memset((void*)u3P.dit_w, 0, blk_w << 2); - u3P.dit_w[blk_w] &= 0xffffffff << bit_w; - } + _ce_loom_protect_north(pgs_w); } /* _ce_loom_blit_south(): apply pages, reversed, from the top of memory. @@ -792,15 +811,14 @@ _ce_loom_blit_north(c3_i fid_i, c3_w pgs_w) static void _ce_loom_blit_south(c3_i fid_i, c3_w pgs_w) { - c3_w lof_w, i_w; + c3_w i_w; c3_w* ptr_w; size_t off_i; ssize_t ret_i; for ( i_w = 0; i_w < pgs_w; i_w++ ) { off_i = (size_t)i_w << (u3a_page + 2); - lof_w = u3P.pag_w - (i_w + 1); - ptr_w = u3_Loom + (lof_w << u3a_page); + ptr_w = u3_Loom + ((u3P.pag_w - (i_w + 1)) << u3a_page); if ( pag_siz_i != (ret_i = c3_pread(fid_i, ptr_w, pag_siz_i, off_i)) ) { if ( 0 < ret_i ) { @@ -814,27 +832,7 @@ _ce_loom_blit_south(c3_i fid_i, c3_w pgs_w) } } - c3_assert ( (u3P.pag_w - pgs_w) == lof_w ); - - if ( 0 != mprotect((void*)(u3_Loom + (lof_w << u3a_page)), - (size_t)pgs_w << (u3a_page + 2), - PROT_READ) ) - { - fprintf(stderr, "loom: protect south: %s\r\n", strerror(errno)); - c3_assert(0); - } - - { - c3_w blk_w = pgs_w >> 5; - c3_w bit_w = pgs_w & 31; - c3_w bas_w = (lof_w + 31) >> 5; - - memset((void*)(u3P.dit_w + bas_w), 0, blk_w << 2); - - // this is safe so long as the south segment never includes all pages - // - u3P.dit_w[bas_w - 1] &= 0xffffffff >> bit_w; - } + _ce_loom_protect_south(pgs_w); } #ifdef U3_SNAPSHOT_VALIDATION @@ -1034,6 +1032,9 @@ u3e_save(void) } #endif + _ce_loom_protect_north(u3P.nor_u.pgs_w); + _ce_loom_protect_south(u3P.sou_u.pgs_w); + _ce_image_sync(&u3P.nor_u); _ce_image_sync(&u3P.sou_u); _ce_patch_free(pat_u); From 1b5e808ec5768112632f2a6ff318bb36343a1487 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 3 Nov 2022 00:21:00 -0400 Subject: [PATCH 014/136] u3: implements full demand paging for the home-road heap --- pkg/urbit/include/noun/options.h | 19 ++--- pkg/urbit/noun/events.c | 126 ++++++++++++++++++++++++------- 2 files changed, 109 insertions(+), 36 deletions(-) diff --git a/pkg/urbit/include/noun/options.h b/pkg/urbit/include/noun/options.h index c1fd58c40..260b7fd30 100644 --- a/pkg/urbit/include/noun/options.h +++ b/pkg/urbit/include/noun/options.h @@ -22,15 +22,16 @@ ** _check flags are set inside u3 and heard outside it. */ enum u3o_flag { // execution flags - u3o_debug_ram = 0x1, // debug: gc - u3o_debug_cpu = 0x2, // debug: profile - u3o_check_corrupt = 0x4, // check: gc memory - u3o_check_fatal = 0x8, // check: unrecoverable - u3o_verbose = 0x10, // be remarkably wordy - u3o_dryrun = 0x20, // don't touch checkpoint - u3o_quiet = 0x40, // disable ~& - u3o_hashless = 0x80, // disable hashboard - u3o_trace = 0x100 // enables trace dumping + u3o_debug_ram = 1 << 0, // debug: gc + u3o_debug_cpu = 1 << 1, // debug: profile + u3o_check_corrupt = 1 << 2, // check: gc memory + u3o_check_fatal = 1 << 3, // check: unrecoverable + u3o_verbose = 1 << 4, // be remarkably wordy + u3o_dryrun = 1 << 5, // don't touch checkpoint + u3o_quiet = 1 << 6, // disable ~& + u3o_hashless = 1 << 7, // disable hashboard + u3o_trace = 1 << 8, // enables trace dumping + u3o_no_demand = 1 << 9 // disables demand paging }; /** Globals. diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 46e2837ac..4aac7f148 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -7,7 +7,7 @@ //! - page: 16KB chunk of the loom. //! - north segment (u3e_image, north.bin): low contiguous loom pages, //! (in practice, the home road heap). indexed from low to high: -//! in-order on disk. +//! in-order on disk. in a file-backed mapping by default. //! - south segment (u3e_image, south.bin): high contiguous loom pages, //! (in practice, the home road stack). indexed from high to low: //! reversed on disk. @@ -20,8 +20,8 @@ //! - with the loom already mapped, all pages are marked dirty in a bitmap. //! - if snapshot is missing or partial, empty segments are created. //! - if a patch is present, it's applied (crash recovery). -//! - snapshot segments are copied onto the loom; all included pages -//! are marked clean and protected (read-only). +//! - snapshot segments are mapped or copied onto the loom; +//! all included pages are marked clean and protected (read-only). //! //! #### page faults (u3e_fault()) //! @@ -47,6 +47,7 @@ //! contiguous free space). //! - patch pages are written to memory.bin, metadata to control.bin. //! - the patch is applied to the snapshot segments, in-place. +//! - memory protections (and file-backed mappings) are re-established. //! - patch files are deleted. //! //! ### limitations @@ -65,8 +66,7 @@ //! ### enhancements //! //! - use platform specific page fault mechanism (mach rpc, userfaultfd, &c). -//! - implement demand paging / heuristic page-out. -//! - parallelism +//! - parallelism (conflicts with demand paging) //! #include "all.h" @@ -726,6 +726,34 @@ _ce_patch_apply(u3_ce_patch* pat_u) } } +/* _ce_loom_pure_north(): track clean pages at the bottom of memory. +*/ +static inline void +_ce_loom_pure_north(c3_w pgs_w) +{ + c3_w blk_w = pgs_w >> 5; + c3_w bit_w = pgs_w & 31; + + memset((void*)u3P.dit_w, 0, blk_w << 2); + u3P.dit_w[blk_w] &= 0xffffffff << bit_w; +} + +/* _ce_loom_pure_south(): track clean pages at the top of memory. +*/ +static inline void +_ce_loom_pure_south(c3_w pgs_w) +{ + c3_w blk_w = pgs_w >> 5; + c3_w bit_w = pgs_w & 31; + c3_w bas_w = ((u3P.pag_w - pgs_w) + 31) >> 5; + + memset((void*)(u3P.dit_w + bas_w), 0, blk_w << 2); + + // this is safe so long as the south segment never includes all pages + // + u3P.dit_w[bas_w - 1] &= 0xffffffff >> bit_w; +} + /* _ce_loom_protect_north(): protect/track pages from the bottom of memory. */ static void @@ -742,13 +770,7 @@ _ce_loom_protect_north(c3_w pgs_w) } } - { - c3_w blk_w = pgs_w >> 5; - c3_w bit_w = pgs_w & 31; - - memset((void*)u3P.dit_w, 0, blk_w << 2); - u3P.dit_w[blk_w] &= 0xffffffff << bit_w; - } + _ce_loom_pure_north(pgs_w); } /* _ce_loom_protect_south(): protect/track pages from the top of memory. @@ -756,10 +778,8 @@ _ce_loom_protect_north(c3_w pgs_w) static void _ce_loom_protect_south(c3_w pgs_w) { - c3_w lof_w = u3P.pag_w - pgs_w; - if ( pgs_w ) { - if ( 0 != mprotect((void*)(u3_Loom + (lof_w << u3a_page)), + if ( 0 != mprotect((void*)(u3_Loom + ((u3P.pag_w - pgs_w) << u3a_page)), (size_t)pgs_w << (u3a_page + 2), PROT_READ) ) { @@ -769,17 +789,50 @@ _ce_loom_protect_south(c3_w pgs_w) } } - { - c3_w blk_w = pgs_w >> 5; - c3_w bit_w = pgs_w & 31; - c3_w bas_w = (lof_w + 31) >> 5; + _ce_loom_pure_south(pgs_w); +} - memset((void*)(u3P.dit_w + bas_w), 0, blk_w << 2); - - // this is safe so long as the south segment never includes all pages - // - u3P.dit_w[bas_w - 1] &= 0xffffffff >> bit_w; +/* _ce_loom_mapf_north(): map [pgs_w] of [fid_i] into the bottom of memory +** (and anonymize [old_w - pgs_w] after if needed). +** +** NB: _ce_loom_mapf_south() is possible, but it would make separate mappings +** for each page since the south segment is reversed on disk. +** in practice, the south segment is a single page (and always dirty); +** a file-backed mapping for it is just not worthwhile. +*/ +static void +_ce_loom_mapf_north(c3_i fid_i, c3_w pgs_w, c3_w old_w) +{ + // XX mingw doesn't support MAP_FIXED clobbering; + // will require explicit unmapping and related bookkeeping. + // + if ( pgs_w ) { + if ( MAP_FAILED == mmap((void*)u3_Loom, + (size_t)pgs_w << (u3a_page + 2), + PROT_READ, + (MAP_FIXED | MAP_PRIVATE), + fid_i, 0) ) + { + fprintf(stderr, "loom: file-backed mmap failed (%u pages): %s\r\n", + pgs_w, strerror(errno)); + c3_assert(0); + } } + + if ( old_w > pgs_w ) { + if ( MAP_FAILED == mmap((void*)(u3_Loom + (pgs_w << u3a_page)), + (size_t)(old_w - pgs_w) << (u3a_page + 2), + (PROT_READ | PROT_WRITE), + (MAP_ANON | MAP_FIXED | MAP_PRIVATE), + -1, 0) ) + { + fprintf(stderr, "loom: anonymous mmap failed (%u pages, %u old): %s\r\n", + pgs_w, old_w, strerror(errno)); + c3_assert(0); + } + } + + _ce_loom_pure_north(pgs_w); } /* _ce_loom_blit_north(): apply pages, in order, from the bottom of memory. @@ -998,6 +1051,7 @@ void u3e_save(void) { u3_ce_patch* pat_u; + c3_w nod_w; if ( u3C.wag_w & u3o_dryrun ) { return; @@ -1007,7 +1061,7 @@ u3e_save(void) return; } - // u3a_print_memory(stderr, "sync: save", 4096 * pat_u->con_u->pgs_w); + nod_w = u3P.nor_u.pgs_w; _ce_patch_sync(pat_u); @@ -1032,7 +1086,13 @@ u3e_save(void) } #endif - _ce_loom_protect_north(u3P.nor_u.pgs_w); + if ( u3C.wag_w & u3o_no_demand ) { + _ce_loom_protect_north(u3P.nor_u.pgs_w); + } + else { + _ce_loom_mapf_north(u3P.nor_u.fid_i, u3P.nor_u.pgs_w, nod_w); + } + _ce_loom_protect_south(u3P.sou_u.pgs_w); _ce_image_sync(&u3P.nor_u); @@ -1048,6 +1108,12 @@ u3e_save(void) c3_o u3e_live(c3_o nuu_o, c3_c* dir_c) { + // XX demand paging is not supported on windows + // +#ifdef U3_OS_mingw + u3C.wag_w |= u3o_no_demand; +#endif + // require that our page size is a multiple of the system page size. // { @@ -1108,7 +1174,13 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) /* Write image files to memory; reinstate protection. */ { - _ce_loom_blit_north(u3P.nor_u.fid_i, u3P.nor_u.pgs_w); + if ( u3C.wag_w & u3o_no_demand ) { + _ce_loom_blit_north(u3P.nor_u.fid_i, u3P.nor_u.pgs_w); + } + else { + _ce_loom_mapf_north(u3P.nor_u.fid_i, u3P.nor_u.pgs_w, 0); + } + _ce_loom_blit_south(u3P.sou_u.fid_i, u3P.sou_u.pgs_w); u3l_log("boot: protected loom\r\n"); From b102cbb908b22eacb74631c613eb24ff1e622486 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 3 Nov 2022 20:44:15 -0400 Subject: [PATCH 015/136] u3: adds printf ("live: mapped ...") for demand paging --- pkg/urbit/noun/events.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 4aac7f148..48b32d151 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -1149,6 +1149,7 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) } else { u3_ce_patch* pat_u; + c3_w nor_w, sou_w; /* Load any patch files; apply them to images. */ @@ -1160,9 +1161,12 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) _ce_patch_delete(); } + nor_w = u3P.nor_u.pgs_w; + sou_w = u3P.sou_u.pgs_w; + // detect snapshots from a larger loom // - if ( (u3P.nor_u.pgs_w + u3P.sou_u.pgs_w + 1) >= u3a_pages ) { + if ( (nor_w + sou_w + 1) >= u3P.pag_w ) { fprintf(stderr, "boot: snapshot too big for loom\r\n"); exit(1); } @@ -1175,26 +1179,29 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) */ { if ( u3C.wag_w & u3o_no_demand ) { - _ce_loom_blit_north(u3P.nor_u.fid_i, u3P.nor_u.pgs_w); + _ce_loom_blit_north(u3P.nor_u.fid_i, nor_w); } else { - _ce_loom_mapf_north(u3P.nor_u.fid_i, u3P.nor_u.pgs_w, 0); + _ce_loom_mapf_north(u3P.nor_u.fid_i, nor_w, 0); } - _ce_loom_blit_south(u3P.sou_u.fid_i, u3P.sou_u.pgs_w); + _ce_loom_blit_south(u3P.sou_u.fid_i, sou_w); u3l_log("boot: protected loom\r\n"); } /* If the images were empty, we are logically booting. */ - if ( (0 == u3P.nor_u.pgs_w) && (0 == u3P.sou_u.pgs_w) ) { + if ( !nor_w && !sou_w ) { u3l_log("live: logical boot\r\n"); nuu_o = c3y; } + else if ( u3C.wag_w & u3o_no_demand ) { + u3a_print_memory(stderr, "live: loaded", (nor_w + sou_w) << u3a_page); + } else { - u3a_print_memory(stderr, "live: loaded", - (u3P.nor_u.pgs_w + u3P.sou_u.pgs_w) << u3a_page); + u3a_print_memory(stderr, "live: mapped", nor_w << u3a_page); + u3a_print_memory(stderr, "live: loaded", sou_w << u3a_page); } } } From 0f7feef5c7b52187cdb6a33062927bcdd2ae20ae Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 3 Nov 2022 20:44:58 -0400 Subject: [PATCH 016/136] vere: adds --no-demand argument to disable demand paging --- pkg/urbit/daemon/main.c | 20 ++++++++++++++++++++ pkg/urbit/include/vere/vere.h | 1 + 2 files changed, 21 insertions(+) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index 486e49674..b9151f957 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -153,6 +153,15 @@ _main_init(void) // u3_Host.ops_u.has = c3y; + // demand paging (ie, file-backed mapping for the loom) + // is not yet supported on windows + // +#ifdef U3_OS_mingw + u3_Host.ops_u.map = c3n; +#else + u3_Host.ops_u.map = c3y; +#endif + u3_Host.ops_u.net = c3y; u3_Host.ops_u.lit = c3n; u3_Host.ops_u.nuu = c3n; @@ -246,6 +255,7 @@ _main_getopt(c3_i argc, c3_c** argv) { "scry-format", required_argument, NULL, 'Z' }, // { "urth-loom", required_argument, NULL, 5 }, + { "no-demand", no_argument, NULL, 6 }, // { NULL, 0, NULL, 0 }, }; @@ -266,6 +276,10 @@ _main_getopt(c3_i argc, c3_c** argv) u3_Host.ops_u.lut_y = lut_w; break; } + case 6: { // no-demand + u3_Host.ops_u.map = c3n; + break; + } case 'X': { u3_Host.ops_u.pek_c = strdup(optarg); break; @@ -2146,6 +2160,12 @@ main(c3_i argc, u3C.wag_w |= u3o_debug_ram; } + /* Set no-demand flag. + */ + if ( !_(u3_Host.ops_u.map) ) { + u3C.wag_w |= u3o_no_demand; + } + /* Set profile flag. */ if ( _(u3_Host.ops_u.pro) ) { diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index f02bf4a46..dd505d085 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -313,6 +313,7 @@ c3_c* puf_c; // -Z, scry result format c3_o con; // run conn c3_o doc; // dock binary in pier + c3_o map; // --no-demand (reversed) } u3_opts; /* u3_host: entire host. From 6c4b10281515e75afbbc9e501e339e19ad158456 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 18 Nov 2022 00:45:01 -0500 Subject: [PATCH 017/136] vere: supports --no-demand for all relevant subcommands --- pkg/urbit/daemon/main.c | 58 +++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index b9151f957..a49dcb785 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -1351,7 +1351,8 @@ _cw_cram(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, { NULL, 0, NULL, 0 } }; @@ -1359,6 +1360,11 @@ _cw_cram(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { + case 6: { // no-demand + u3_Host.ops_u.map = c3n; + u3C.wag_w |= u3o_no_demand; + } break; + case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -1430,7 +1436,8 @@ _cw_queu(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, { NULL, 0, NULL, 0 } }; @@ -1438,6 +1445,11 @@ _cw_queu(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { + case 6: { // no-demand + u3_Host.ops_u.map = c3n; + u3C.wag_w |= u3o_no_demand; + } break; + case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -1515,7 +1527,8 @@ _cw_meld(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, { NULL, 0, NULL, 0 } }; @@ -1523,6 +1536,11 @@ _cw_meld(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { + case 6: { // no-demand + u3_Host.ops_u.map = c3n; + u3C.wag_w |= u3o_no_demand; + } break; + case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -1584,8 +1602,9 @@ _cw_next(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "arch", required_argument, NULL, 'a' }, - { "loom", required_argument, NULL, c3__loom }, + { "arch", required_argument, NULL, 'a' }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, { NULL, 0, NULL, 0 } }; @@ -1597,6 +1616,11 @@ _cw_next(c3_i argc, c3_c* argv[]) u3_Host.arc_c = strdup(optarg); } break; + case 6: { // no-demand + u3_Host.ops_u.map = c3n; + u3C.wag_w |= u3o_no_demand; + } break; + case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -1648,7 +1672,8 @@ _cw_pack(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, { NULL, 0, NULL, 0 } }; @@ -1656,6 +1681,11 @@ _cw_pack(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { + case 6: { // no-demand + u3_Host.ops_u.map = c3n; + u3C.wag_w |= u3o_no_demand; + } break; + case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -1712,7 +1742,8 @@ _cw_prep(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, { NULL, 0, NULL, 0 } }; @@ -1720,6 +1751,11 @@ _cw_prep(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { + case 6: { // no-demand + u3_Host.ops_u.map = c3n; + u3C.wag_w |= u3o_no_demand; + } break; + case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -1883,7 +1919,8 @@ _cw_vile(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, { NULL, 0, NULL, 0 } }; @@ -1891,6 +1928,11 @@ _cw_vile(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { + case 6: { // no-demand + u3_Host.ops_u.map = c3n; + u3C.wag_w |= u3o_no_demand; + } break; + case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); From d5f33747563d9a58aafc61dd5fb438b371a2d791 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 22 Nov 2022 16:09:40 +1000 Subject: [PATCH 018/136] landscape: sketch of staggered deployment --- pkg/landscape/app/group-store.hoon | 58 ++++++++++++-- pkg/landscape/lib/gladio.hoon | 123 +++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 pkg/landscape/lib/gladio.hoon diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index f869a4c0a..444c46cd8 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -31,6 +31,7 @@ :: /- *group /+ store=group-store, default-agent, verb, dbug, resource, *migrate, agentio +/+ gladio |% +$ card card:agent:gall :: @@ -38,6 +39,7 @@ $% state-zero state-one state-two + state-three == :: +$ state-zero @@ -52,9 +54,15 @@ $: %2 =groups == +:: ++$ state-three + $: %3 + =groups + wait=(set ship) + == -- :: -=| state-two +=| state-three =* state - :: %- agent:dbug @@ -72,10 +80,17 @@ ++ on-load |= =old=vase =/ old !<(versioned-state old-vase) + =| cards=(list card) |^ - ?- -.old - %2 `this(state old) - :: + ?- -.old + %3 [(flop cards) this(state old)] + :: + %2 + %_ $ + old [%3 groups ~] + cards :_(cards [%pass / %agent [our dap]:bowl %poke noun+!>(%migrate)]) + == + :: %1 %_ $ -.old %2 @@ -112,6 +127,7 @@ =^ cards state ?+ mark (on-poke:def mark vase) %sane (poke-sane:gc !<(?(%check %fix) vase)) + %noun ?>(=(q.vase %migrate) poke-migrate:gc) :: ?(%group-update-0 %group-action) (poke-group-update:gc !<(update:store vase)) @@ -159,6 +175,10 @@ ++ on-agent |= [=wire =sign:agent:gall] ^- (quip card _this) + ?: ?=([%gladio @ ~] wire) + =^ cards state + (take-migrate:gc sign) + [cards this] ?. ?=([%try-rejoin @ *] wire) (on-agent:def wire sign) ?> ?=(%poke-ack -.sign) @@ -192,6 +212,32 @@ :: |_ bol=bowl:gall +* io ~(. agentio bol) +++ poke-migrate + ^- (quip card _state) + =^ cards-1 wait + ~(migrate-start gladio bol) + =/ cards-2=(list card) + %+ turn ~(tap in wait) + |= =ship + ^- card + [%pass /gladio/(scot %p ship) %agent [ship %groups] %watch /init] + =/ cards (welp cards-1 cards-2) + ~& cards + [cards state(wait wait)] +:: +++ take-migrate + |= =sign:agent:gall + ^- (quip card _state) + ?: ?=(%poke-ack -.sign) + `state + :_ state(wait (~(del in wait) src.bol)) + ^- (list card) + %+ welp (~(migrate-ship gladio bol) src.bol) + ?: ?=(%kick -.sign) :: TODO: check queued watches don't get kicked + *(list card) + :_ *(list card) + [%pass /gladio/(scot %p src.bol) %agent [src.bol %groups] %leave ~] +:: ++ peek-group |= rid=resource ^- (unit group) @@ -243,8 +289,8 @@ |= arc=* ^- (quip card _state) |^ - =/ sty=state-two - [%2 (remake-groups ;;((tree [resource tree-group]) +.arc))] + =/ sty=state-three + [%3 (remake-groups ;;((tree [resource tree-group]) +.arc)) ~] :_ sty %+ roll ~(tap by groups.sty) |= [[rid=resource grp=group] out=(list card)] diff --git a/pkg/landscape/lib/gladio.hoon b/pkg/landscape/lib/gladio.hoon new file mode 100644 index 000000000..4a70607d6 --- /dev/null +++ b/pkg/landscape/lib/gladio.hoon @@ -0,0 +1,123 @@ +:: Migrate scripts +/- gra=graph-store +/- met=metadata-store +/- grp=group-store +/- i=migrate +/- *group +|_ =bowl:gall ++$ card card:agent:gall +++ import-for-mark + |= [her=ship =^groups =associations:met =network:gra] + |= =mark + ^- imports:graph:i + %- ~(gas by *imports:graph:i) + %+ murn ~(tap by graphs.network) + |= [=flag:i graph=graph:gra mar=(unit ^mark)] + ?. =(p.flag her) + ~ + ?. =(mar `mark) :: XX: correct detection? + ~ + ?~ assoc=(~(get by associations) [%graph flag]) + ~& missing-assoc/flag^mark + ~ + ?~ group=(~(get by groups) group.u.assoc) + ~& missing-group/[flag group.u.assoc] + ~ + =/ writers=(set ship) + (~(get ju tags.u.group) %graph flag %writers) + ?~ log=(~(get by update-logs.network) flag) + ~& missing-log/flag :: XX: doesn't need to fail, but suspect case + ~ + `[flag writers u.assoc u.log graph] + +++ scry + |= [=dude:gall =path] + %- welp + :_ path + /gx/(scot %p our.bowl)/[dude]/(scot %da now.bowl) +++ groups + ~+ .^([@ =^groups *] (scry %group-store /export/noun)) +++ network + ~+ .^([@ =network:gra] (scry %graph-store /export/noun)) +++ associations + ~+ .^(=associations:met (scry %metadata-store /associations/noun)) +++ peers + |= =network:gra + =- (~(del in -) our.bowl) + %- ~(gas in *(set ship)) + (turn ~(tap in ~(key by graphs.network)) head) +++ poke-our + |= [=dude:gall =cage] + [%pass /gladio/[dude] %agent [our.bowl dude] %poke cage] +++ migrate-start + ^- (quip card (set ship)) + =+ network + =+ groups + =+ associations + =/ ships (peers network) + =/ dms (~(get by graphs:network) [our.bowl %dm-inbox]) + =/ import (import-for-mark our.bowl groups associations network) + =/ chats=imports:graph:i + (import %graph-validator-chat) + =/ diarys=imports:graph:i + (import %graph-validator-publish) + =/ links=imports:graph:i + (import %graph-validator-link) + =/ =imports:groups:i + %- ~(gas by *imports:groups:i) + %+ murn ~(tap by groups) + |= [=flag:i =group] + ^- (unit [_flag import:groups:i]) + ?~ assoc=(~(get by associations) [%groups flag]) + ~& missing-group-assoc/flag + ~ + =/ chans=(map flag:i association:met) + %- ~(gas by *(map flag:i association:met)) + %+ murn ~(tap by associations) + |= [res=md-resource:met ass=association:met] + ^- (unit [flag:i association:met]) + ?. =(group.ass flag) ~ + `[resource.res ass] + =/ roles=(set flag:i) + %- ~(gas in *(set flag:i)) + %+ murn ~(tap by chans) + |= [=flag:i =association:met] + ^- (unit flag:i) + ?^ link=(~(get by links) flag) + ?: =(writers.u.link ~) ~ + `flag + ?^ diary=(~(get by diarys) flag) + ?: =(writers.u.diary ~) ~ + `flag + ?^ chat=(~(get by chats) flag) + ?: =(writers.u.chat ~) ~ + `flag + ~ + `[flag u.assoc chans roles group] + =/ dms (~(get by graphs:network) [our.bowl %dm-inbox]) + :_ (peers network) + %- welp + :_ (migrate-ship our.bowl) + :* (poke-our %groups group-import+!>(imports)) + ?~ dms ~ + (poke-our %chat dm-imports+!>(p.u.dms))^~ + == +:: +++ migrate-ship + |= her=ship + ^- (list card) + =+ groups + =+ network + =+ associations + =/ import (import-for-mark our.bowl groups associations network) + =/ chats=imports:graph:i + (import %graph-validator-chat) + =/ diarys=imports:graph:i + (import %graph-validator-publish) + =/ links=imports:graph:i + (import %graph-validator-link) + :~ (poke-our %chat graph-imports+!>(chats)) + (poke-our %diary graph-imports+!>(diarys)) + (poke-our %heap graph-imports+!>(links)) + == +-- From bc787310d953cd4e75d66c1c429de9cccb17f974 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 23 Nov 2022 12:43:11 +1000 Subject: [PATCH 019/136] landscape: fix wire consistency in migration --- pkg/landscape/app/group-store.hoon | 15 +++++++++++---- pkg/landscape/lib/gladio.hoon | 5 +++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 444c46cd8..e4c16ac71 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -66,7 +66,7 @@ =* state - :: %- agent:dbug -%+ verb | +%+ verb & ^- agent:gall =< |_ =bowl:gall @@ -87,8 +87,13 @@ :: %2 %_ $ - old [%3 groups ~] - cards :_(cards [%pass / %agent [our dap]:bowl %poke noun+!>(%migrate)]) + old [%3 groups.old ~] + cards + %- welp + :_ cards + :~ [%pass / %agent [our dap]:bowl %poke noun+!>(%migrate)] + [%pass / %agent [our %hood]:bowl %poke %kiln-install !>([%new-groups ~zod %groups])] + == == :: %1 @@ -175,6 +180,8 @@ ++ on-agent |= [=wire =sign:agent:gall] ^- (quip card _this) + ?: ?=([%gladio ~] wire) + (on-agent:def wire sign) ?: ?=([%gladio @ ~] wire) =^ cards state (take-migrate:gc sign) @@ -222,12 +229,12 @@ ^- card [%pass /gladio/(scot %p ship) %agent [ship %groups] %watch /init] =/ cards (welp cards-1 cards-2) - ~& cards [cards state(wait wait)] :: ++ take-migrate |= =sign:agent:gall ^- (quip card _state) + ~& migrating/src.bol ?: ?=(%poke-ack -.sign) `state :_ state(wait (~(del in wait) src.bol)) diff --git a/pkg/landscape/lib/gladio.hoon b/pkg/landscape/lib/gladio.hoon index 4a70607d6..a6d59a85e 100644 --- a/pkg/landscape/lib/gladio.hoon +++ b/pkg/landscape/lib/gladio.hoon @@ -48,13 +48,14 @@ (turn ~(tap in ~(key by graphs.network)) head) ++ poke-our |= [=dude:gall =cage] - [%pass /gladio/[dude] %agent [our.bowl dude] %poke cage] + [%pass /gladio %agent [our.bowl dude] %poke cage] ++ migrate-start ^- (quip card (set ship)) =+ network =+ groups =+ associations =/ ships (peers network) + ~& ships/ships =/ dms (~(get by graphs:network) [our.bowl %dm-inbox]) =/ import (import-for-mark our.bowl groups associations network) =/ chats=imports:graph:i @@ -109,7 +110,7 @@ =+ groups =+ network =+ associations - =/ import (import-for-mark our.bowl groups associations network) + =/ import (import-for-mark her groups associations network) =/ chats=imports:graph:i (import %graph-validator-chat) =/ diarys=imports:graph:i From dd04bd8f79e9f23d40b667eccb0655b12f789d96 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 23 Nov 2022 14:29:40 +1000 Subject: [PATCH 020/136] landscape: include club migration --- pkg/interface/package-lock.json | Bin 1184223 -> 1020411 bytes .../views/apps/publish/components/Note.tsx | 1 + pkg/landscape/lib/gladio.hoon | 24 ++++++++++++++++++ pkg/landscape/sur/migrate.hoon | 5 ++++ 4 files changed, 30 insertions(+) diff --git a/pkg/interface/package-lock.json b/pkg/interface/package-lock.json index 9aa0880123499cec087ef679fd64c045da2561c7..517b3858302ddc70dad6a83e4eb02f9824b2fff9 100644 GIT binary patch literal 1020411 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

tG3v%OoEUPO6>SnmD43HQ@> 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%0Dtp^B$1 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

>T5hExINCs%U7fG`AO{TXgk`5s7pioDCPn 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 literal 1184223 zcmeFaYm?i^l_vcC{S}J(v^!oDRVAq;IYvEdciW!!xZUG!x5w^@l>;O}5>+HY0c2gY zyZ-I(bMk(GOd!Dm>mrqRW7HB8K<2sUx&EL3@7c4Xb-K!qe*NqyUvIN=mQJ#x|3&|N znUz&utm&7Na+isJ+oY3=bT0nw|KN>y zCw`Ux75uDN=KZwzWsz3jt*dRiT>dmE^Uc=zocMj2PqK9-j`RI@e-T?2|31w&*?O9- zC%Lm7@yq*hI?k4(a<|^*5)L0;AD^5azcLSFukUC1dY+Y=5->WiM(l3{pbsyO@&DKp zWjf8TlU+V4vvjgerg^$7=3?#hW5AQI6-z35{d{zljm5IJSa!meZI{J*l&`1xyhx^L zc_Ee&Zq9s|-fiA}0?r^lzBg;hS!m&rLHm|pg>2B2Lz&bOm^vCP~5~P&Cfx2~jZ6q_%i= z{PK-^;K?$rDk64y1{{+EZcZS8tC`*$b*FSr+M3ec{FN8OI~>MN*}p zUIm2b97waGT;=)zC$-;ppFZ7)$LQdUl=9U|z40w@$ab_WrfIbhkG(uTd3}7!2xe8R zvbDrM4hwp653?$ERkkhDje!=Uc=K~IUl!wZnT$brYH5vp*Em+X%s*wx)gsH5w1|j} zeXYn=<7}F2(y}tpdw%@V-O#2iHp%v8qtJ8m>KNy77ku1hs~imK4y^zQC5 zukvxe%(pj51=(u2qb8lojE*Xy1-#T@K|dyAoZ&)qUyYRI^N^EOItCkUi;HXxnkvO| ze2c#jDqwN}>YZd-B+)k+xm4R5yvN~JfS~>ote7T~VznvO;1s#DSNImAUGbu6Qi1j| zNe;AxaZ9yZPtxT?kit=JyTw|{a zqYyp(xO&B=%qHn%p_wGO4$E@QYvDzpw&0s?c2ag=BX2o%ecAy(N?g1+#$k?x`hWK9 z|B+oF#Pu}0{I*Lco5Ko)e8RX(sqM{jH_z7z$&R9eloB#t{531LQr90zea^rcc)A#$ zXVBF!W3~yjah6JnCqM6sqR{i3Ei5M5#sZA20Vf`3s*FDlGG14t8yCa;Xr%+OSf9<2&E1R;#OpGHS|fhUXRuOct{B5{V7*DX2a! z-rrZ-qP!Uw#lSP97P?@g`sG7iHdE zFz!C#}h9@Js`Iuu50A??1>!r=8UJdOy%cbHc)yUo~ zOn9{wDTB#~@+>(AXNv`=XE5HFMwIVx)WsXnBvUAIg_1Z9S&;3|EE%@2EQ)QCL&KEX zPN-RplSNsq(rqra$E)Kvd|#7gmacsq*3cl6vVehV#X?P+OE@Yyfb%o(F#Ji=NUy9J zS;EM5z(jn*UbWb+mcISs#=v#jVpm;Alb7@ldq)eNh};sWmwBel(FQdviwk{)IDNxE zDJ^0c1<=-6<=f>+{WzO+J6XVR6<1jS6WlBK%|tLifF^{YSOW5^(O-qThxiFrNbPb- zKe=`a%m+?*MiwSga!xZ`#*E~wfo@wRD_jaBu{B_0Sx{^SOvg!ANpkh$%NmtvIX_uS z02Ae(Fdoq>px$9Emij7$WdLl`3dq*b-t`;#8uu{T0L17CZan5GVLB)a;^_EzwBD@L zg|p#PcyF(xS^&BJ41V=I%C1QTXSfjIv)X$htdybL2{kxI+*M)cY0DPCtKHrBjgZM^ zad2)%-rDx6?f!jPESI~@NJ&B=?F7NRsvFp~l~7WvVy#>Vr$n1<>$TNH24spA%S(BQ zIU$Kxb9K;laij_612j&R+hud2Fbn&=*u3qMVo%maS$(ab`g&FI*-yZfzl^n&P(S zQJA7jt`;6XOgY}X5R%LiAAZOKXDN0s{M^o~yUNLCDrVc!EF&$SUE+?BRK>1@6P)~< z*q^gQ@-mji6<8xF%EU^Yy4a(8YfX4-Fd8uQz5l{4ba`9fAmz2q+I*m%mUT4@lEgr&tyceR9F=iVN_@@SpT>klL+!sofxLlGTNxxB!Ynq7Oz=_a599ec_T4NDc*2q1b@G3X$aQv=Tp z;Fqq8?E-wCj&YA8M=c!~f3L&!rC(r@toX;OnC@`v7`<1XkCF0k8QICgUao<01Zja| z+}1SJ-xWd`HoOfEkR_%?6qEG+jnDJ#Vn?1z-`2U4`%bp5r7FpSVglDOLQ%$f*a*{@ zCCY%^q_~8nTVZS}?*PS8IQQz;s%Ic~iCd80W6BCyx+kD1qJniCP#-4q@;$d zsXPowx4ZRP25UZ$Ksbxq@oEE4EBQ#_09>V;jT9CBExSUxW6NLTdcDY!+7m(DeqQF& zd3Kd8m!o;MPV7dGa1=-_CU%|0GG4%!Lu8nfYce`&y_SCU4oWi;!yb5v+lrM1&i43O zd_bks4NV8{ogI){IW5andSyud2Vp7oEMSR{#5B{958T)-=?@N0p%cN_xEIb!nY7@8 zk}zwm8aUef62Cz3+y>CqYqgk+KV8s=!G)_n-3lHjg!i{;K)zxhZydX(gSA)|L?>PM z+^UF01yHc>9$PXsQV%-`?N0t2PN^E04ylN6#u>K{lo<`q%gPk-3Gp@oVD1f&uS2wV z1D#L4_IwGCErJ-*ypoc?>p7|a6BF)np7L#8e$83r+md5K*64zCq1-m2mK$tW-e|DO z4+CtA><_cb#h>#o6Q38KW41HQBD_K0pGYhVa3XHOku%N4JL$-WxZpnl5!KCFOB0Bn zWSnhN?wUETiuEgnb|Bsr_Eomh9!Gfh@#@<#t6}lk4oNp6>p&AbAVT&$ZKHsmnvgY! zcosZW`XQ{?mBRQW{a*cgG~x@7K}I8iAhXLB%S+R9nQ{H0fuZ;1xmA~^@go^qVt?CKk8k$C#!MV{t{l?$pkIcUP%=1C`PWD$;=?? za!`FAaKzu@8+1}UA{!p z6{R4hCFO`w_+v$`8nBd*+cL32$nO{c40D}gN+G-6AS6hbpv{RC7W@;g2|9<8vJhM> z5QQoE?8?-;gQ{PR1;BTJ+o%?4uDv64GyuhTOMywz{3TM&U>(5$SBPv2&Am0)D#sC` z(<&5(P***yb|7uB$+EIfZcF54f|1(G0U{g55#0@;X#v|uAGFwH zY*!w6)M9lhgu;25mxbhKksoEvuo_M|AwVgM43b3WFB6BouBDN%?X4&Yu}myqBFik% zuQ7(!Yp;o@HcK?O%8^}%0P@m#Q-BKjj>`;o7&6wX9mJOTUh5&N4HEC@J8(NCxMwX> z$UBvlttHCsgs@g(M|ViJC9-s{H%T=0ukziIQ`kJ=|498Z zR9nSwFcv`gqRb0A#)<;+N#3R?+RCCbkkvM--AET>w-|5QL|<0L29o~PrC%`n$OQb! z41|!!Wmx}%)*l^~S75`g*d0`)usa2P<#yshJgu`{d>$hO2Cc`mIt(1!Rgky12*kM* zT*H=}0Cxk4MM3%2muZ=k8BUxYfCVFg5Y2W9vU|dJr!f;av}@iRj_&Uc+YLn!IQQd5 zwg??^*J0qCZt=mO_|@WePjkJ2r|z3U1?`R!j&w4?HCkKPF8ww%e2lrQ4c^AX_GSr- z2)X97TxCl^y}8t5AKcfoYHwlAyIf&ONW2?i*>a%?5SR;!K?&@h1ZmAG44m4{tvgco zn@B*`-d)@cpmxfeW2B{?SI0#;A90J-{mv*Ed2G@Igof`NZ|9$kZ&6g`&P!BZ5d&0< z^wr5}^78qQH`lMfd$an>ub$7-KV7fh{NeY1`Q>*%fA^O^eVqRB>-lv4eDSwW*)%z=#h)+p&DrIf>Boy7|NT?->Ey4!+x+h9^oMW${ZH%Xm;dpbKfeCv)786o z_NyKb*+Gmq5k`j73{HQ#j7KFQ1Vj<*~^D7cIFPtkyN;T_g3r zj(w4%RG7-<5dWk&faB65R#fh_05sg3$3Z0sg9d%&j))-aBMd^ovdJwjfv&|uR>9pI zH(;jS=BNizZFZj~P!Y4o9y0eJ+pep!;GV5VYfvUq^1klQXp_)!&WKum!$Z=}Ej|iY z-3$uneeW*T5pNYN6MT1}{hY+C0Dbb1!ELP;%gdctLzI3mb&ro){@i&b+ZxEbtgOK~ zu*@|1K>;wiSw2Bc&`1X{Z$;OYwM=4Btv&Wi42eyrl)*f8k3tU73=MXwbOuLXl1~vG z!}V)7K_nEZItbA$5&ESREwl$P!o=DMVXq*koF3x1(^IHKn|l$|cLnj$+tGsdYaBL^ z$Dx1uM)gS`^dlpaa{CG3QsePP2muPbKX7Y+SvjTN+tQ6ISNXqErkyr>cWD^ z<~`v(Hcy?gojH=cvzrUM;jtE7_5e}c;(t)+wPuc`6NO%QEw<^k@?IE7Xiw!El;hkJ zW+`#f-af=8+9F7GYV|Zxxi9AJ$aAqg6l7*~I2QE1XK8zT+@Svf8b%~z>c$t=WQPs~ z#30ArgJqyDP1wXHXE{lsH7E#TpuTkX#C51Jy9LuBxu+1hj4cB6V3z@fkGn*u`eef| zDX{1sv_Uod1$j6DF=%_G2<-ThN=>9VN9v%1Tuq+cg+gvtuX(C(`o=BA`Sb=Ea*!6L z2~9wlA1tub!tL%iEuw3-e!AGUyBj5?o9$VH}$XWA|hw&g_6qz9!ejJu%a`$&f9v6_-3 z-k^D3XOhI#pRfP?>H6>4A3py3WVYFS`pviN)yd7*e}8`d(~lQF|Lw=qPj7zA{_~f= zm;Z6~{eR5AU;g&ZuYNiEJ z#^c5>28mo!d8qA6bYIww@hCD9v*NWy6^kbxC+lg{kYS8w88;$YD%#G6&2R>wLG`MF z@m@Kv2Sja9T`G=k1){JH?lBB-OZOnAMTibZhCoEo={*vM!gH`@SHPQ*=;3(SbNjv< zXL>l)>zAw){MA%^2#)obsot8AJB1F~TB93YkW}5q_FoK4;$!KocKMFrgODW(rW=~{ zPN~Kl%k^^=I>OtAu6G5ZN2f}ZtYRU~06dx4zAJz}KI=_Gv>u4`L-mW4bN+CkA3&^{ z@~)`v(FG+H(e(>S-V5#Bw+5Lli>sj3ftElAZXV;Y#pFjDdbL-|rlql~I}-D*1fw^md8ag23yb^7?Or6C&F8xx zZalB9p9Gtlc!`!Gd!;oi{_6Gq;Q2x+ z$r}~*L+FbnARBv%3Y31~FzSj{ev#m54Tktx?a7a(Cs9Wj?putdUt*XNG99l{F}?c< zs1)lsGCsKa4C4BcLYMWu344nVmYe@sR?F zSY@xNU&20ccl5dQ0p&c1QfM?nRed-goExQixm3bPh2&J;hO+JMuMyUMo-8YqP`{NF zvo_UhN5mY0Q+y&`m{E^Z6xEIY0AaiAeh9F(j3#nF-HEUjAu{1Dp%Tho6Hb;>%zN`>b3P6yy+$D9_NrK8LU9WgZ5QK1aK_=V$VZCQ z+y4tqQ}aQ6Ie4#bDm1lj$NOQ+N1Btzx^@3u(MpdN)?W_E>^>t||47v?Iryg-r@K*& zyXR%G+f-eCH>TYVA;O~^Z)_LwTKWDkWo$FCg@~TO#t=!?*;TSl%XtX zbM6nKdvr;$Fjd)+VjgtZTDdPfzUjVEu6}>d)<!P8B3 zoGnM_?yLjZxAixcSvtWq_#6S{^rmCp?|li6M=FseXz#b|X0JpGb&Zf!X2xsaVn5zK zX$pQNo0Q*Pg2%e;o9lH*!mfGj^nmd)NC7T3D}>1IK~*aRSku8JRCCFE)Ii?sO3eCQ zuMv0eC_Ht2VTaJfdokeWroJihVGTEaYah--VHQSl-NXBruWPd0w3=hIj#f zaCoQfkr(O_Vxi7Q947RCsmfNDYMlBRf((x~m$6zD<#v&-8P4&+s#YJpZ$o#Q#W;|3(^iu4 zB-VS0bq&|2(gES{{_5S?@x6?}qYLylK?Pb^{vS-O4mq&G-Q;a#nTp=oL=f40MBLY) z2wg6>pHVlA+fUoJ;<9N=^N;iyDjo4wibEBZ7*s_?+{UZLN`L#j$J5fc|Kq;drGBNQ zfMbrqbN8aKXPyl;lkBtqTDP`7k9PFn>hr4h7FGS;)Eqk`o^NFc15$5?Bk_D3I)fd6 z`s@L{=Tkit`-9pCBi`eIB0!d?}L7x|C#c5uoHE6_3RA{O;m0SnHT$8jR+ti~W zwq|{~+aLBZwcD~f^t6|y-DXCi2iy2L=6PSY&v;;aBtgtaW2AuFcQ17KZV?c(8(#}I z0fLw<1erhP%dMQ>?*+Co7HvJ|S*Yb5DPop(T4B<9=BMH#8K5Ao_&3-izwC6ky z#%(lZ>Gg(SClK+LR{3wYVP7KqUG&<4?ds6)F>ghEuUFK|#>K;}D$5?ZRvRrIjfB>; zUccLG)8Qi3K6^0rdg{!!IZi|%WyDF>wy0yXu#WO- zH@2>ybc!m}VL6Gt zK59gIo$MmNT#w}eOp$V-5wh;J??7m+570~iso#fraHG!$eJ*_Qy#LbPV6VELy4Sw7 zy=)zLeYUQjW=3{^M5HT4F|#lbkXzv-dt2sVnO`*^v_+fmJwQ!y^5nciyqrdjiZi}A zK85k`2EQL`6kFo&q0UKYI)rZHs?FUjp z!sn)ijL}K1w0Y??SGN+RAtOE3rvFJ-_S9Bnm7VB&iq@s4rV>UI-_fBvtV>LayiG_K zA0DA-nMra-nB*U|h!}*YGk@H7}@PM=*B!)%iZn&aZhThdC8-aBP@XV z#gHI7^<&cj5STgYn#(qs@!Ck@VMNAciB?eHnrtKNpqCJs%nX`go;ON1CkrgfX=g_t zNT@P}uu3o7U#Wee7Pa8nO^(cB5pzEJK(DuTgZQy3cI5=qi_=Yl(zp#0?!+gs3Q?-2 z;C_(Z2_lkAUl@QKp`}^2u0*)>`|ti@|9zTmvh_4uPk75jz{t6zCH7lKAAqSBP^-6- zO5*9Zn76+6go)C;8TOCb~{tAQ(FJ-8n^D#G@)lJRc)LyDsiE2DTz@@Eo?N+5C#{} zdnwwnI`VArX06>U*xvQc-TMCg*DwKd2%~L2%Y)3eEs|MUZF#F#tsV}@aaDFYLiX?$ z6V(ygSZ{4G!^eEkMvg}7P@2E_>D%Nq`DU5!z|3uUnH1Rw$qSPLe4oxqn)|+u%LjVw zXxi!cHfe*FPfiwPu}XLOXjx3s<#xATXQ6CIQqV)svP!S>LWEdxrW5DwU?;;XFOluf zj0XgX*SnIoc|=`}*>5*!Fd$wC0gVke)z~;+J7C3kmcll`KaZB%WAuLMOJazXIp66C z>Jc0Jr=bx*x(lEPtUCk}vH@nrlDa^a8H})OvX#MZKoka*fy1p%q><=55y$H=2U>|E zz~|<>pLd#h_Rt1)^*jCdeEK5jM;Vyp_gfaOk^Kz+JJKOq(A>7%Ws0~^Z=}hr4ZQ30 zBK?G!F{3M*?c|WH+XcYo{HEhf&*kcz*mET+m~y_F+FvUpqI-y7|+BH@&xNd$Y_#boU$y7WTfn$J^l) z-7d2#{QiA+UoBzzj56^@ISF78^2Ws=>e{i8q?=)b{zy&F=_N-W&M<4)5oeGyT7}Rx zzozUniq=W|zZP0pZvc-md-{dQA9jAt=WE?fgPaFvu3%FsO*6P#rRam;<3|Ds&%tVP(y_H6)l59b4^I<7%^ zJ45{sSoF_+`0?2{-#kyC`~RmQ-F8v|d00_yK6O{{ndS3plTDzGSF%ZbNObkOS4Dcd zki{Tm3z>zsH)#tt^={ijEr;0Xd;MVsrlSw^Eo-oWg5Tpv$ub>RBfUkrsCQ7xc^r}# zqL3uX2gGwC1quAP^Q;v2Wf&^#Zvo|j_OPz<=&vcZV70NrC*8rd#B9OmJS}mxFJP_a zvzx%aYGz404|ZIt7wIM+EsE?S@U6E@S#{W*_$XZ84iWYFsF4<7 z`TgEK^3exW>%=Ld#cq|ZuNDQlX~|e$g98#|b*3RSk+Ex8q2m<&ZaeS!W_0EiVic=X z(3`yEXlQ^9TTBz55uhnmzoA=TACn0lL*eaP_bDAZjP;y!05X;aaUD$8J|I$6l$s{_ho3>OYx%ue zrpC>&j{tr~E{KDfH)WSJ@fr4ru>X_nYU>U+DDIG?(bxVKdK2eY`|urJ z?#%3{o9OnECyTU7X3I2m?U1%+k4>1R9T1E%#KO>PFozFcFSa@uVuA(452WA z%!K1_nxH|X%E4@cR4&xFe|0>ITWyyTOV2XE$m?wJLqI(Cyi2iWL@PfB3sK>FaxN>{GuqcGY|zX*{4kU*9v+qoG|p zX+VP*@?$dJq~%7LCsswl#@OVDD;vE|j$G?pFv_9}Ajp-`H-qk*Ssf(CF7Sba@3#Yq zk+C4kWf8UniaLDCw2`1_c6fxJycvr7YSX+*(@9h^NkET&IhR9jjk#u#k8YHqWGJ|u zj~-?EjGzdopavxEgG9~T?_i;ya}LNY{5rD_dLy5~z_ef;#Xu(DmiCe(^jw za@z9{YcLy<);Xhj{W5|f+YpF8Qz)o#v3qcMxz4M*Bjs_kW=Y&#omd}HsQ_4v3l25C zcjR2tHz@7!bO;`Sp(W6l@LDWSB3_Fh&9yw38P@4x(=lbl;l~Iim|8H8Gleb9*KnZh z)O;~{D&~e_izT%P0wu$XxoFrzTT87Sf}z#&l>y^Z$f77;UxatAE{h!0iSJjjsNDSwIAqUlZo-Cnr=iBmVZ;>S%Oh%wiFXBx|}dvmvL{T4L8z5b zKTusa{xHgrqmtywXB)~55OY00zz6xyB$(R@fxGnm1UB;U9YBGNYcVgBIn&TSY9e)F zb#@0Iv#eYr`fCv@{S`98MEgA9p;t=BEfb>!db7BAm|QN=5rjLE^s)#8<<|Y~;v?z- z1bi88lfdB{riLa5w}O~_W7b0LAi7A$o4S^UDTT{T5T8oJO02ypi1egkgVk*`l>Ql) zq~U@NPXQ&Z4PfnX@N!Ftox=xqS+Z(&-0rsW^LocApT#pz=WY*j-$u)cWtHnTPK|p= z=S_+2ngkWu>j%GR^DC+kwFd!Kk2`d?6Rw>x*=?}>GJH4CuCk9iG_ewQx*))#6=yHc zC&#wW8`MdrJ?_5yq>&sR1tsnR=y^`%EJ5f;YmivzH>{P~^+P3Nwa_|i7py5>Xcd`S zqGc~>pKzviP%*(+b5n~%*CI91A{{R?ZV|cSqJ-ar1kxkKUhL7y>u`{(i#KYf>LM zXf>Dlf!f0gjg%oAm7lc>BLwj;Cv&A00(a@c2yEoxyD$P9XUf6Px^Dl~jtgWY>prAJ zmH=G749|j&a{3T|n0t11kHQgvs8bcWbK6f@J>0_N&B(?swCwYuR}o>pPf zNV<#LCKcdc~YXy-ti zn70Sh)m*!hDvNybom>u16U|Utst$+yK6`V4?%Np^%tm1N5Ib`tExxVsp0LB%{VvIH z@|Og&H+{uXii{|@bnQF1pmfLGTqBpbPt_U1_aVXF;ScrXZ_WlXoy@+R0>G{?2y=J@ z^EDdO`A!FiuG8zYgML^9tOdgOvh0eNqX$#A%v3&{|NA|P_@cjGT;!v1SzO6^629*u zW8IyH*m3a43-kLWeHiUqGdPy18!sfYsIw-%5#NhJ8@)q4cV|1VFeCsi!-VUXTI1Sq zZX23ur_+2kOK{xHBA-;yl=EC9kl)$PXB9eFfse)3-`4Y^=Cb`4E)H&`P1Ko|MR8Fj zm2BtmK#s%qRKNa4oobU`XG>jy^gvF<_H=}HaqupY zyet|E>2!+bUqVi6H0YOj&}9z~i7*J(%=hEa7PYsySH%h}m+vlj_kj38xlW#7DC}`~ z48w6-Osc_sTMXdI5?t~f3Bqsq2QwD=7V6CA(4g8R1{kf;;!n0YK?WVoiEFMZX$C@; z5IvPM-1!C5L13UMu)Dh6JU5n3`KHsW3XQN=<0;u-nj!88d@TT@ZBA8 z+3(s;mp2R#n>sWg*R(*U`z*fgyh8iR5Ihxc;+=2}H^D0F@`ho^)NekNc73i||I0z)BxUGz4L=~86 zlU})lyIdBh;wkIK=wmU5Z4uF(_#%^CX>onyvI{rXF&bduhP^`n@~Jp?BPw}?OR4OA zOg~wWXmoT|cSU9I3Y)1LiW>1g5y{ax4E#9OsK=v=BajT~J|xIxsk|gN67obx^Q6L{ zmUJoYwnmpJOdE3E(11T`)zKQYZN+-CqS2?eNj1NFVV9UU4rzfH^@85pCf)sLlzii?|Ktu1z z2Mf7NLvM#*``e>cn>4-y5Hh1{03xf%@(N1Iv981kFg(+MsnNell(p+^QV%C#OeXSQ z8jp8ybzJ?FEBB?-EP+i}7Wp*ER=Xwg+TE$3uHkw>FIvlGLf*iX$*4Bmvb_CXP*BfO zfgy@hvkN|rc$fAw#rbTeTqGIto%ty4e!y41lWJOoy>}%IRoS3f6I=h|^81f?sqgqG z-aUR}JSu&pX9wByiN^YTNA14W@oqNDN>gIO^)z6!+s=|Vaxf@ntSEz;B!-4l@Hc{CJ;e^0ymlLjq5CU;)M-reit<+74L8~fz**k3Q#!1uGcj{ovxMfS!!e=I z2xgc2v!GAC1^Ll}ZBTwx45YR~!p+biF=hj(?WU<=a+MAznyn=kqkF&#PCRLZg7$pC zAqi~R*9RLbjQ?95=b3qvFlDu1mgA`%@&w)2M6z#!HH>k@O*E*SJ9FSO@H&+lLO7aG z#U5(kxw^VKR_lb^@B0nv|A?U}l+E7u8Ts1dqMVOZvcGc-1+4$AUl2@r?_f_$MSGxM zizGklUGeTieJxwqoTTB*(4WB>$D_$I_x7XiIHyfF+T)y#={3y|$H*zKlvvSsaBlqc zIe1t<`>Y$)NkI{U^#xgu(z^qvXfEPbi$i{OUg8UfGH)C}S{qhxlih5%Svw3ARav9h zgiw~;*$6PH6$0F1MZ<7*PQXb)XM$=n#@kj?B{-Us%Oye`gSOch!@;?-x1@0#dYQ0& zHwA285U2lm&MS?P{20loc02JA5WlbtLqC&Av0KX+r@X^ExTZS;rWR%7Vnx;u;`A!x zhgLFU4LUPjZWbvlyc~_61g09V!y=Ru<*Sz8#fQXqXbcsqijZd%d>J~yPP>-TwQ*Wp ztrI))M>{K=g9^(#$<|xYnKFqROoGDWEEREWS+Pa0fpzJM8%)Rsn6|}yE(Vc$K7^pL zh>3GaK(Y)!&}|||AQ??)5lxm>Q3Hs(HIlScP?~;Z8*q35|R$m?);r*WoBn(-w^ z6bJe1dYh>%2KL~QgTD@TVo{BTHaFi9VF~p&4XHhpGrOhu_6`%& zxv+KYot3v}vhPY!fd_^h9=A9Is8KmTd?(=kkd6~Xy7|W7AHTSOLrjx#LqWO*zo(ZI ze#Vs>UP1VI#LF9Ov@MI>T&4px_*Jl&cud+*8lDCX581Sl^2BO%A07!B)@#)Vj}QOk z!u)Uz6(0a7ICd5*Mi?PK5&-1kTLH-Zb3VDFJ$;zxxr3h}>1%`9cliDJK71wYzN$DD zjv!0V4aHd^_D_C%n0C^hlEVKH3Sk=saL(hy%l{;r0i2U=Ckwxd~w$+$^&32~u%RR>N? z!q2%nsAzHI(bRL#DA z_fDGs0kD;;<$6Ljkke^i;`WTW(G%g$@zKaBn_%6CpTc5^T1+P^8Zdn`mJu;Bzb#&H zuckp$6r&aCc`}(v25B3Dsl(SA5_`me2_$lbEFbIJj8P?3_(*l8cT8=iDtBxFvnazr1a>zP}5Nv+|p49TEMK77NaIP`0 z@?n@>Zu15@cqQ_D%W{`YFcw~lSXs@Zw{Nf)k2jK~hE-|zICV9W-61jBvEeIU}nxcQE7f=*V`yF)iW zuz|Yoz!DFJZrhXbBRS+zjv_85TlCr4j?&$B0TbyW3)NsE5o%92Pnc=m>#tJ~S67(vPadc0ZCy#Y7@GlF!YS8NkS4{E|mu%tDbo_)*$fl=^5hx}guvs*>hk$izpq zs5x`22+yh{D@&E4*0Al&8|yIDaKQV<8D>48CBY1VwPb-FCS)bz2zZ$R7nZ??LJ4H3 zZQP;NRg%I7Dr1CP`nL#VjU;&8HwCUN0f9{KI~iIc#{k0h>g#E)jk@XK!N3Joa}z#d z=G*EcqC1pJe27P5(^9l=qc4%`Y{{t4Mk548olWeATsCPT{o#;Fc_is37a7?>Lz8T6 zyT=G%L@<*|n%kfm8cJv~+-il~@ZkdXx0|XN@qH~Nur%0ERMUYA<`)QT8FSXWq}?ZF zI8+xWef7@TM)80Ux%OJZz!R`t6w?qwPzev!Z-m^T0ipo`;siWPv}Q0VET^#1JuL%z zL#&swwCv-f7Or;&hK)v$P&Go}8!Wi2t9HmVoEu+q*ShpaUQPFfO`%GSE6CaD;DqkW ziE#HR1$gFMoA9OKghl;WKJe3J*=}!O@Vl^bL$zMYtbwV3M^6BVx}sVxkKj;s>_@Ke zH0*pxgQZ3=(L^;c*6KP2-g~K01niOX+k%75YPGsr$nJE|5i^L%MRJvmk-v3;Tm~v} z3YcTP(n=!jW--xVv0W`iOBFDd^jxEJDH?(r@PQhs;|f}viW+%H%vmm~uf)aKf?2GC z1$&OHjGapQegV6Ki2z|FqdkZ?FO~bqBr>v?ZKNJ=LfY#z*~zGMwCuTj?Fm+EKGa)jz`9x6+JLtfE7A3>RSmkmU*N*N_rn@|4AgtSrGbRK z-&Dg=+nhtZnjd``Yb zFsNwqYyAn3HpqiLhe^0ZFjg#Rh#3P-xIw4}O%sM;?2cRX8&(W9BstZThYy+u%=RF- zB+jUCN*G}JIz7J>5QY7H%A<_ALBwMZ$V#o|*qsI?2g{t(I!6=1py=$L zx$xTYBzCaIUP--i5zjK6<`_AeBdC?HkP_e0D%drMg5_E*4h314kEpb1#fF@05OvgD z!|YQUD6k)qut#2zVE7Mai({@FX2XU^+b{GiE$5BN$uZ)mh4LEbMmyH)kcX9yY>ng3 zL56FV``Ju)f6RH=~*#q!>u!DSPeh%Sk-bzU-!c|hY=thLDWynLEh z2t!w5M)5Mek;X7>iDiM&`RC2dW8fsiXpEJu5`1e;^mBQJwps zE~K(LsPm1uAXYRrTz{VF{QBn`2x{GIwJe0GKznF?En*|M)2@;Jk2)zi6Q+eosh%=5o0NNRs1WKXgn>JiPg3p>pF&#~ zyKR!8)!M|UpTQ)*26a4}8eJTM(>+jXf5JN~R2`;LdT=0p>R2}+@LS8|;R=ca(DhgE z$fVihmgLe-2&VTy(%jh8Y^n+C&Xls+MhGwuhS=^6Kt#%Z+bsk$YtG#ngC2n2YBvZC z|9Cw@d#b7b5X3$^lTV!8cR{>s2xxiYvHm>xQ5K-awSM@{gb{72acy}58B$ivg!TH( zl|WXvb?zEscStW8$`L4Wxk%bfc~&;GhZ~S=y2XeJAN3vGeIs~jTcO%Z_*rD&|5gy- z4x^?$>9~Q~q_5cX6zqevt5lXf2MEDE%swjW@q!0)gLi21x7~`SQU?W(B~p^AF35!} z_FyZ#Zc}W~(?{et1mD#{tte62n4DF(2C4szHS8WTITvG*fdRjlxElKqR!g_7aeF7j zSkU@jUW4}j%wjIpVI-?Fh8MJ87>$|{x=_uh;5X(X<-tlT1C@iqz71{PzRzskv}>@lG$1}jccTz)VWey+W@@#9C35+1V3R!m^cuZ z2DdRVM1y;rXq2we&Tktm`hSby)$QD=a+heQPhUf`gHra3{3n02CQ`F6ows|W7T(e@ zdq&}Tc%DSh4E5j9Be&TRT58t#k@nRw1_TajG!z`tde3%Mrn65(CS7115CI3v;9&>D zU}%cSJ{a^XK>MUkJNi2e7=v|#q5hxH)PcOCVEqpRZYkq3~bReA!eAWN` zy>Zcp+jY&=9ddi0R48P52gR+13k1F-MK^(=?LjR8S`b&IE2?>&EOr_Cg^lEJQ90j( zZXe37_TiE`$~fNM_LB1Gs)-vkoOC`+r2#M=nzF1TJ=L}k(ZFl?@tT?H<+Vts)rXDF zPydurn@nfdac_vhPCiAl{>=!@hLHI~{uupgH^~~q9-Ks^y)r0Uq~8KXpQA;Q$)ah@ zJa^F1Mp$a=sJy0OQGu;0o)E@P*nFfupEl$i z#uy2B^qk(Rh&tKOQK3W3Kowh`r5EVuj3L2gX#2dJap3L0*ZZ&_q|7m@>y6#bJ zDrI9nIcl2(63#XW+C-?%>6G+udrjd()PF?%hB_&J&#TYTIklP!Pi_hnMl4hl{+GD< zvyXS)>!lLT*VP6rrutIQ$Z;FV!#Ak+nwqUdBe_kcH#`__vm&hAX3LDq4u}Xo5Nok| zgDv%sM*^j+>+FR zlqQ-Q4X-@Oimd$BaT$1S6AxNes0{wFh3B%ymnkTu{n=@n?IS2eBaS$0jzp5s>s)q2 z)j1~Q-iB7Yo#eAUo+JV3ZD(S8-GX^+l3-d)?op5O0}TkQ1a-@1hri>)vrss$|I6~8KZAi z+-ef@ltLA?y8E3miW|<-ICb2m+ge3^ye#&~Pt6UQ7Y{d{+b~sMr(Sb<^;ee#U?$qmUyE|$ecgq7_ z?nqMH21Izni(9?|;2HSgHnCSg*b(t-ZXS4^(NDbcp{aby?t z<9RK-HMu@OYxs%b+L}6>ONL74u@}lGh7)lJHwq+pL(FmPwg94|R{a=GGa9t>ldAN1 zM88~B>3DaZShvBtdk1hkxa>&*3Q#MzFr-qqS4k^mO{LL}r2cnljh!BMOULc0H~8SL zmyg2a+?(UtE|w9wTAg>#Cyu6GSHVb_<{52iJa&#IQ|&%DIU?YJ(bns%JLLapyh-*l z(Z>9IUWG|CL8np#u-y!jLs`ZR3jacLE#tJJpyPXBpvmXU9cI?@kr>;|)28B>sFgPk zT-_0&ggbPs)-A9X>A2Y`WHikS#l#0D0S;5S@x=eqW{>56K8(dlU@>&;6Av^q+){Q& zibv!`WKQ)6XSnLZ9lJ#po1Oxk#>^XY0x?`#{_Sw{r7dey$tNW-n9gf<^FCP0<7lqw zfMsME-Lk;&$V|*d6WrIBR3;{-W!IQ+Bf~?o^G}0$Itqp3DkE*7O&u_Nn?B(1jSmBy z4V?7^9s!vEF&+5e*aD4730SW*?bC|Np$@$*ZdDB7*)s$sEV>0p+uDmvWs%Dt4!v~e)7serH^_J!CDq|dIBxL>OPWyej>Z@+%UfE9n0ZibS4cNRIuU)o8R51vx2HjQ&rX)O zL^z=k<02AWIdcw{dL((bB^OxGTQ%Ei@CsY73yrN;OZ&-Ppin?&JPl|h1>djgJ-6U|=|IPWlMNTTLNz2>~lF(}5WdN)vOSGZYkJR7Cb6x*hq+}MyS zfS%eadZe&Zs9%%WGP@q{=2b$t*H#j)F|Jvv*E(RPig<>HGQD!qKs0GhXK_g~{TD&4 zynemf-f$$Mc9p*!**{1D^PC#!0X?~!^<}(j^^bi(bMt-H#>)gN3Y>S?{IDYiag970 z1InkUVpcCruWh~qU`Ku&P9{6yVRo;cLQD-UYIxA^Zwv?(IvVW-d>^>X%PsmNB&%Y& zTPh3#H^5FqqkByqk~Ty`GOih1h&cwH?zpmX zPCF1&X!Ot^^*0cNR!;S~^));n`ZL1uA^)P8O-rYvj{DwoHbia!7M-N{e+v?rKE_06 zX;-mzoIcW{^nSfrA-*yavpq>`)XW9_`wA_xN!=)`k&H%Hicc1pf+p(@8y#e374+FU z-=xZ%Xe?kh5z~qJdIt4|S1ZGkw#ODM%N=?JNtnsrHq5SI-=(HBY}5Kzq~iRQd)*A@V2bXVOFnVWf=01khOK z^95-Si79p(43$OeNbwuUgrA|RN^uHZ32n_&U|Ni0d}`2={QTI(^KB6F;~`5gQ#8Nh@cTFAHhX7ja{xN)yxsY5#_xN>;LMb> zO~H<(=Ec|OEXQOJ+&fFzLXn1>GA3W;n0$(k(i17Zgdl9lN%!}flr{Du4T7&-p||PA z+|3%=1Eb36Vq9)yV>$shYql6o5u%KVqa^|bubB2aSbL}>F)?8nXIOF5yoskx($Q)&dxoVea;^4 z%!)xYk3WLd2~-`K^g&hHC59UYVN;98dkSZ9wPVdNKLA|c>M&$w9T()$b~Ua6bhMxv zKu0S8aH!Zl`k_LWhuQ5QWWm)YnMSp{^SvYxv~!QSB>I5F4gL<&VJ1shBC)%@XVOYT zj+-5l9QVrbeKlNOr71d*2?9iawE+kbUG1|~nzT*3;ZBDRm@*9)hN+6mQA^)MZb97o zX^aZ%?3k%RhuY5lJ4z9+{o|+AN>=rMMh6o1e=*f^+ zMC;RZi4LgAIc}F#-9hjA#)-wFjr!mG^lkDY`DU47ili%gLSsP6bd9+Sr*9J^uZ!oUpvK=OKLCIku`7!IKPYo2D~i@fVsEk4o)T9p7y})`|~oN z&aywASjQF29UAQAk zQ95M4i!|yMnMhBFSb$cMHMF`0CRf71c}>ZXtgd6-FTkWl!nR`RL+s=^oH^`wi+@-% z$Ukt8q2tH$9WvODu^kZ~E(nwbxm{ydE6bq==Iz>k)UV3AE0 zY+c4PclRwaz5hbGpb8qO!mhemPZnjdE_TSCbdql=k>G@x`j3(0jmgz+=H5x7WSo+& zbqc#H0n{q%8z$1j7vjGDqIYTJ)X>;omlEA7%8{n6RS;4%%62JW@NIgXERZ-QlBcBa z1Q`Xjn|vz;v`8Upxa9u^xeMGu-MGug3$0)#`F+yf;}a*cD(kMKh(n)(oMht)a<%YF z1t_7ul!m)X(v(ZTbTn&H1E?G)%)gi`pZ-0fJBtJAT5#;PEvZ!-Z|%6*>vcj_)ng6= z76ouZya!G=v_VhMHy8SLMsu$=F)JU&9iT@wV#{^Gcuhuu%D^-)$Llc$Bqh}%UrGNi zA`IL?oT5OFhIgwz;^Y;i^gP#3+^R?$rj+Jm@Ok^Fuoe{@JqNX8HX)MDb&6!8LhVT( z2Zo$F2)wPJz8g`95Skc;duO6BqCi|OQ6tN!fm>0b=x(w^$pqa>H(9yORa98km%9w7 z=az}uKe1dqJ13DyX6)s$HhuSi=w~0-YseTG0G}Z z)C^8ApgEx<&|dnzs2HE4U=Wkr5J0OS2GeQJ=QlK)n;^MJQGv3SW1_@4UPnN1Q|6b{ zB4m|q7llfchz5dwSfTF*)f;){He5{a> zMCJr;cr&4#+ASd!go*3W(_^GJT_C-rz>s?fZvK&ZlJxAS1?5H(tbJ+n%#Th(76$fs$ zmmrIQFS!pq2?V&zAlesHH^9%zse)^`Hf(C7CrPoh%th9Qu~Yz5^kF_;7I0{xf?C&wI5i^mMCP0a z_w_(KuW(GXm(9EM9foZ6o*MUe;8$B_z=oA*tvy@y+w>D^z8^An2JZCA#HQURzFR0; z1N(;(%as2{=b7q1yT%6!VIYh2c>afy-ivGrONwkWOua)ogOWWkGA?tO7N$4ldV5J6 z*;vLvNexo-s10Gun|zs)xs{-#be^f6X%JXoKv!n*gUG%T%Di#4FykryjFQkg>sBZ) z|3@IANFd1K)#g^F8G4rpc|t;&Nmh9mJ;sUx_LF=8B>=JfTE*((4R{`RM)#nOlEW3* zmtET)lu?H!iV$CTP1WW}&%C2BJz2$VHtHiCuAe(dCo(aVYdrWnU9h>zO6tmnE3}ce zXinf1$az!8RaV3%*Bxdg?&q$gAnerVSO+sHDc|xVPMWh zdy8I^35ZNoC^-U*tx#$sgyaicjy(wLD`{bt4JK)|>F+94&oBs(-_=bs{t3$fiPU{%2o>`TrR&*- zd{B^EG{UO8I_`Gbagm~7+%hH8%eNa@Vc26gW@)rszTN)yJ=C{(9x>@dPz|d_f97bJ zk4Fe4?_<%}mbAA!GPfUm2j|z^J_ipMx*_RZ*ZRsO^uxGxygpAaEq_9aVA*7?=jVzH{=_m|@PTEJI@w z-fz3gc+qRJfbI%V@25FJJh<=NpsSK7D28KKfQ@Pfxf90TX=`aTFX;h}MwppS+Kprc zk$n-jRL5w$-)cy!ed#_ahDg{(szb39ZmG88)zW}E!7)^%#3F?LOL&Rewn@%qM7v~z zkmkaD;x6JOHfsV^=FSC?Xh6Qy?>6UdLAlcLPa{0g=rlb>n1>nyZbbVh5e8sxyK!-y z$)8Zp>)foc6@3lUhJ-dC=%*!eTBwQD3q|?0sz~z3+NxXX5zRhI*G~%4!1pPh$0eCp zPBx{H`|*t&Ie>dB-|!THNd&)5+_%xzxFa^y8KX(X6aoS>_s~-OW-K=)(q!!g6|l-> zk3zaPG5obB?=X{$^@@#`YDTaVunRGD|NJAJXJi ztJA&L$PI<8Ht9sSc&o+69L8axx-02aM_+U<&96;<#~4`xIJ+W!0-w-Vh*)SU^?R>~ zVd){>vDw_EK`XVAPQLK-YVGVOyP9&`D=E}@T}`>{^*SM251nHwZ5Qt7cFtjZ^gh$( zDartLAt7nzAvur}>ca^}e^apbk85*HJXpSM zJ*e1$kNUnSN4gIHPmc7x#`*=h^^0+&`-rJw4*6ulXXHrt!Qjeq4ET=ET_PQlIx?N8 zp&}?d!&L~l&dkenlFfF@M2!}qkdDU>-=X&F9VX+nhj91$fB~=7@o9o&>2+G(9KU>Z zb~bxCjaOum;Hf*q&`3i2RATpUJkP#UaOdqXC~c-2Ql$vAv=emh*Q+14~R32&pG1d3Dnb}M`&&NulQ>uTStjmv6! zeUhjr!6}qfrf*$7eraB;zPqU-R!$@K6(+o5Fv;B44H4+v+t`9Bq*9kKw5~0&=O0k+xJ9N?~oQIi$Ge z+QrgdWXk}=JW(T?XKN9$<`ClCEU1sdcrvonwzNlZwzUV1Jx!qAey>6|C(PrGkEY{{ zO(>W+jLY*}Enbo#{t6{w9&L-|)S>9wBoAFr>&R*d_eo!-WpGMlM zY2PBnSax-gVxpldyDg?eM;7bakvju*r*xmfeDtG2JC06`Se`0mEEM)FA7}@&?f&kg zv8RcwVqh#wIC+}LO3l0Ie@Awdjp*6WycmybB5MbeDAUl#BGT)p&4FQDRbN_^ybJnj z^s*29JWsC!{H%Pp(=Xf4rP*VYRHzBVuvaCn zOnn{A((MAx+|c7s7K+FOx`rpP<2GPBp6nI#;!rZzW*?9!I%qG_9i+o9G-1+V4eLCC zf-ouRA zj)Q*oGip1|FUPjp<~%+We;yf)?SPO0$Urv4)39EOyNVDkrK~|gjnv=zYHp_vXaLn6 ziW>Wfn8%SjG&#F%g*CYi*E|jDrCQc`arjKxW{^l@*dyv%H=nPp@ za!c#qmqm+ANDKuT;SUlu2>7AI%Ua-&L(0_SX?lqJoY`K1SVeP1D9LZ0U?e~)OUFg@ z(Ap|kwJcJ!u}y4mS02C+E2g@AGQI&#$etJN0&7BBIotn5W1 z{N|!NcKN90aW@77_C*3SKW`0N{$uyJuSH7vaXzPBXhe;eYwuhKf!SGEbzp>b=z zuJcZQ+rT?R4LdY@8UKbiib8v)z4BfW=5C7j*WxUEVW6o*l+<^y#HpNzn|G zgf$)dVf$2Ozq}X;5bc}e6eR60twm}FAnrWm9^B6(iR1?N{{85dYHCGsgV+=ulS(Taiu~VwiRbR%H8%V^JL|#tvhiHgsx3X zK;zY@FHdY;iBoz+`8lZam9_h4$8V>vUOpecJ;~mjyqdk4O-^TT-kiQYdGi)id!9dk z_3Fi|S8r#tvzqJ20h4J-M+ESK`w`q_;pVFHicMn+Gg-V!zQI?_BjqXcqe(%k28Zgd zIN8ZpsyaVY-i`n*mb#>@YhP&ZxY##-g3nGm?9j&7_w$t=NXLUoAS5;#1QZP7ry=*~ z6pWHjnn&pz)%`4c&c&u+MsoMFT{bNk?Ha%y;3GsgJWzsow&4WazbIZ5{cY2^bRPjS z9ID)j+4L?J8%dgCTPXj)3Ks@e7d!P$=^!fQQQ-6DHo2;J^=d-Y^>c2^ICGX(cPZ0&V>?au1o=*?&zi?AlI;l4oZQXB0U8?>6i0|o)O0h(;%uE!u9WS@)CFcigS*W1 zQp(ozD4+&h1@}Y$at*w#Fj+?ZL}rM_pyr{D*_@0KdwSimVR5G~nN%rEJ!v!I(FwQVW?l zEnJ5zO>r&0rv2{0m}rCGLlwoTtgSl?svc${Y#*5M6ZqgJQymN~bdT|JmZAYaB-bPt zi2(ZL5L!;v!0pL&aL#u)Oz+uM7~H%chLD^~W5DP=IM-I2VvW+6YE;3%-LFcf58*ML z;{!8kAv>6H7%-2id!W9g)g@UTAQARHN$bq}}IwVwN)d=@r!5!<`zP8dQs5KkD z5px3J5|Mw~XXazylwJ>9wR6!AOV{1l0ZW$A)i|;Kj5x6TOJ5t9+RoaWOT2->>ByqX zcj7P19_66E0K5ng(qO^;wxUd}$eWHV7XK-pbMASKYfMzS9QKU4*w9!h& zDkyu~I(mQBVov#uqZlMP;ropuH9Fg&umG~kIBTBAh!^8(ihj1qi2LSbu58y!5t z2Fz$CKk87Hy$wgk+Wa`Ws=#-W$6fi+a_AaI`p@qAZaT2YE(>8J>9Li6=!85Ud>)}D zI7IqltJ0XI6|zsVY?EXkcS<{uXO|6uZPy%aC1Wd;4CS9tUq8zy(t@A}I(vDCPS64e z3an*9Eb}ej%8inz!ZqYLjvOfL zTR#S=9O zb>z%F92-|mCAtR^h;vS+o19b}Cr4;F*YK5fdLl=pbzQ&eniZ03kiyyh+W32LsUrie z=dt16)@qctp;NbE9!aT3?DxvM^MLWQjn>~RvuRS@R2X168pF3W zhjLz%-^eG7(Pq-U#4qznww9sG$WR5OzWYJa&q|@Xv#?w3cK7*lQKCCq%Vup3+R?pl zR`ix>*{kCnc>sGAh&c!i6yx&@<+Ev3<#SoiN`4!gu^f$kOww!tXBhR-ADAt(>v2I< z!v_Jxz|1f&g}?VPY!zkGxB@&9xT5b#zRks4(8mDVYLQMwzlTS$Su|xO4e%KDit@f< zUZ$JH%_G_HWm@K`D83AVH!LH~oi?0y&@R683#5<2RoYT)Q>#j<%ho=s7NNXd7V8lz zd-8daOw;lr0DdYrJTqB*b$uWC{$)O2gai|Pz_xJR^8?!9ZH-ik zQLzq6W>{I&gQCpteIpS-map~pVMDApa5 zQW^&?NRU2b6Z3#dwM3MIs};4xOw|KN9RXckvNA;r5L)~%X20|%@eX}dT+z^QMrN_J zEpswT|0N3RE=$9%kkv$GK~@V6y3yTQbv5rH<`#Ga&5?i) zADO&F1FMr>Y}84VivnLv*3jv~FhV0BX!uD8tvpw2P-q#ujL~R#d$Jo#4|W%=#E2Va zs47UzG!`G6YmUTj&D;rQ^teZ5*QDj5PUicts0Cuv^%z`=wlPRKL>&h*0|S&rIfA{| zAmYT%*UM~Elpz+RRx8#*8I-6(Ab=ecE$qxYtfS+K>A)XqqOC~5l=DZ#-ZNsBgT_} zwKw!Lxcbml7~Y<3iv7rDQ9U$~Jq=Gfa9`>JEfLPphfJxK;UXu}Ir<>0^POYDlD7T>9fJhnMAlsCM?MLf7sOV7ZsD_%_>LkpMA>#zymtEg*w|MOQls4DB$ABzG z3!AXFTT2+wPS8~>FMAARUhfl4tEF_Vrh5vWe0qtI&{ihJolZG}11Efo7SBcsMleDA znKfwPq3+>u6i+24chR?Ny4IXn(LLyB;NGEmf=F5C`6OLY@1k@dg5OGjVF)flwvA^Vey1&pZP;3+=W2H*U$Uv&%!}lkWx7K$uz^jNJ0hx1PhL`P z(ozg4@$D2>I#?gQ90F#GowpWF;A1$^vV<5RC~#; zPA0nvHZF$>#4WBDZo0lUDf7)1BeR$JdOMPT*ojC!AY;M|w1FkdZcQ9b9(*s?8}v*; z<`pt%jNN)v`?`_U>`~12*|3fh&8OTE3sCgtIC!BBd*QPsjFRz^3}-Wdi91=a97)%1 z*1f&0;y0=%z21VC(RY02qO*8anT+e%yN^dQl-5mW}6t@7XlO_+fcrevkB z-qC;gUg7QCs9g|z^0B+~TEBn3P>e_xV#dWubNlTHaZ*FOZx~srp$eHbBmgf-xB||i zmQdtqiT3H@zbIDZfna?L%?UB~9FC4{pXeeOF2a~&XJd@VRg0l7A2ad>cM%$1!>p@! zS==}c3m&fo;RiAF?mGyTCE5(ej)O!tP+pchXVgicnSP^Zu-WFFbV>F=&?onNAgc+r z4w%Ij6p0Ouc=(kBl$)vxhV$@egw*vsCB8?DTRV>b8w^}pCO4l{>?C%igaV9VSA?k* zu9WqPh|gNcTB#XthjFphpjFQDH9YvJSh#o%W;%%2--a=4b!HX%l|bRsJSYp970D~;m|?&d(#xY2Q$2v zoYFuX7?JI|_49ne5Cz!%hUJd8W;J1)z1Jth>BG9 zdu_A2Gya#Q7I)=1-;QM8Fdw@3^i$pAN_31}Y`2^0*P{{Kfrw3%H^=ME>byEG%K7NM zdF?2nmnY&i$J_ZQdnbZtHbXOlt&p22M}Kv4n*904<;TVL53gT*{MBau{q(1+-48#U zo&Njs`s>%D)5)uwd))!eERL>&#RB;KkWW= z`kyzyT)dvnUOd14!(ZoT^3h3#c0_g)m1G^+ar3RrQ@9 z()p4(M|m@`6F9cb$4MEiR_e*O&=c$@0kWi!lVwG;!kAPoW{m{2qUcGnp5>t)gD3?qu=>+4Z{Pg%(? z{e5}0Is5n1pFdvxXZ7t*FMj%Y_Cq@QezgAfo8SEESFf+X`>)^5PJXrg@x}aV^QW6% zX1`rum*1T}|L*rcy*mHxAHPdCR~$ycTt7KDyv6~iP975|_`R5GSrxPGNF;(M*(HJ* zi3}AA6G(dH9rI{h7FQ^PO>hO#aqABVjdQPshi|kbZowtOm*^0at;Wln^J)|>M4)h% z4O8qwKJ!dzqq#s<$VjJ^y5JZX4p%Cw)25H3_G6_51`QiDV06ZmuFe_ z`qj(c)E!G|*W2A>WIuxIzsM01$Zijoa?mfc@yY3%!)_DpamX};I|S`xVoXcE#oczc z-v*;8U*J2NiW;c?45sPt@zT4J#x>60gH1Fjn!GFCU2LK(6|vUDa$>dt1Lf8#Nqki@ z{llXHvAI50n3COS0m@E{zoRUAU;gqT=cC*|Hkn!n}Y!Ok_W zJ3-UWwz1SD{fbGUbd6 z1N^h<%>Il_p;tV*rmJ+EeW_7}Pa*m$`yM;~i?v ztSqP|$*dzAOXd0rW_Bn@Ju%O=#nqbnf@J0P22d6`U_~kSLeH~vtEVp02(h3@--fk30Rz~00pEbfYcp;irQ7IR~XUZTS|fH!K;1$+e|nN0v4P7Mai&r)$qK8aQ(NUG6)h%B|#A=uDCGSHTC^(BO4r&cAOpN z=jO#n@0W-KzsdpFbO8PCmPQt0#Iz`j4WL5ibD6IXcNvH&kLLrheJG&T1`%>9Y;Xxa zp*$GKb8SUY%7YdP=HwQYdLIgv1op4qMN`(c5X2P#w`f3Old1c#X84S-k1*j!Suawr zHIC;eE4x`sS^D1^ujqhK^!;>wgW*||oCd;bP`lj&p_ts#REJ%dVC~q=Gd_G-9LDQQ zM9&c9HX;k`vZV(PY(~(Gp_SpdBu=23t|mTskzMI%OziE44JBu!WJglQq1ko_lwrzp z`)DdHMXJz(0z9(GAxBf$c~#!)EU%bPcVR0^T5L9GkydPzX->ICK3I@h!(q_^15$h~ zbO9@VOx;_3-{Hn%2N$}lvgxlNo1-!leg(o^KYiZH~@NzC9p#cC0i`5cd2h5pK6NW$~@&abcn*XOHpO~%Y zDSGWqaq9~17G85JF#21rtO26nW4stxMqDBP{9H{c^iT-KRi*E!yE zgi+eKsw~02vsnOawMMnhdl^DIqzN>L3$nJumkD`Swyk({i0&>n1dBA-$d~&Ng4ebI z;RA1#n95AM@N>ALo=VKni;m{V~1YV;ACZ2Un zSuF@jKFxqb^um@tF>NW5%gx>ru`H{Sbc}0hI?!s|sxpBerOGEtYv46T z&|wLx3gyjK{Y;H4v_v&{@4zpvP97V=TechFI$54C(sFDz(S<%JAwV-D>&b^cGEW`J zetU9(=_xT1`kKW+gwpFeU7K{-pO3>DRyw@#)sKcAtVbr69{LD$FrfOH51w?yUf4U} z7I&fO65Tc||K5$1cRao--rxt{zW1Ooh3qZ6EFMF$%K}lZplA=MgBm7~Ve8txk4Z$; za20NA%xjq9CKhQTVP`pGwo$c zwt)ekNiwA52fo$3Pe%lw2pC=8T;{7>){y(?iau+_UxaAt`W)tq1&8X=Ey~_CBR*yb zF|11t)(**npmSu@Wrrxc^4Izf zRf~i45Stx4w6evVX)pl+ce`AKgk)}x^QG(6 z@>E>Zpnwp{eQOw4ujz^}_b8^2=;GEjKdDay^22v+hfZ;A4$(n7Fr!Zc_6*I9u^{gd zK|X3~mA^4K;#PA>1CpmgWJ$mzSTq>xf(Q|aX4^ZgonW&pJ!0ch-wNNO1a0Iei zSHxNb{14(o%>H;UR~fqUtPpqlkvPOZ{y`1!#H*;7)D8^+UCC3zg*4CC2@I@sJ>JRc z6E?DQa@l z+vrjvq|<0Fv7uA*U^}TB!C>GNmwsf4gp+!AS~MT65TOXnNy5a_%#?STkA>yGlad@=}-^t~+dYruf9u{dusg0qYyIi>T5g^gr-+#{yhVa1- zmg(S@odc}88ETL;*tyZ^bQ*Mlyus;u<$Ia^g$29F-<*iHDl&7P%09{IsGo%_lAE$~ z{}A}9g~nFtMW!-6+8 zMIU4izdbZf5GlLRXm@#FbI?Jw4B8G+4+ZVWkz<1x#NpyevTK;c+EjHlirLJ-GKJGB z)ib{e-9DHHsSk-Z+===#aI=j^lV!e--`rj<_hHJK?DDZBNi)k5ea;(ZNp#%T=?F9y z(!L(zG+PztNQ5>{=SI<#fttuLutF)CG(?+dEh+bdnoGkzpiyb4^=u3Sv`r#;M_9YJ zl|}m;+mV#MGuH|%29XwKM2qU{(^PQLa=?&>*Cih|?@b$loaSrWy>g1FcCKnqX8tQ&iEdsjROWlyDNID2$fh>cAWkpO>R==vjqO@wHQC4) ziohhQMrJu$`!v4T_xgb#jg>X~Ba2&et>E-=QVqfpNR8a;l!zI5l=+IhS8aJCrw2%! zQSF*)sgPEPMIHh}Q7A@l4uJ_7p=j9saYus^?~L|u0mX+(woZ7$$1Z3}v*w#FO0V_ z3OSK#u%qs3VlkQfES$1MFbS7f(W*|Y;9UmIxjXExcymE1338|=ivoKM=&M@5ssmY( zA!~)BZMK5GR*66~Kar(x=tU-W)?5(^ow;`@LNQxciv+vPabv;U2wp|<@Uoc7sF{x| z$dzSjVo*RU!$6JRk;+8JxdE$WoNd$Nmn>bt1O32CGuO1y|JEE=1Y}4_x>iD&ecT~q z0iK=71)6{+I*=gTq4B{dMaZjkQU;SO4sp{|0BWC;HYqBoZ!1h>v_q2_NVAqLuur!n z4proMk^FFz2qKAdQ!H=L-+Kw+S7zr`IBUmsXAerIaJ<6JmrT2uT7zaJ_pS+;>aJhBimq% z-kR*n5@aB5{wPNy@CAsO$0>&)W`%EMVkzvU_;ZpNnXB!{H;(ZNTzMZw7zZ6~giLsi z`U9W2&%ZT6mYJ?@H(eLw9#t+Alv8VCBIbj#F|lY~Y!BAG&t);s(OW}RjnXwQp_bUB zllr`7y;~xTwE+94=$J8~8Lj#?J%EO)2-N3Jk}9M2I@m&wXI1qph!4|zQ9_c+E=O@7 z*Bz!3M7t&nQFEjF%lCc3BF)$Ro<+qAs;e$DAmJ(%!4fhc3FkdUgrZz06O*o!`X!Nk ztFkVAg5e-HY9?mcrQVnLsaCg%814Ab=Qi@S!voQ{8k+~(1@rz;7q!?vK$AqYc12wP zn|-x9`efRktDAO}YG{%KNB_se~A z0d9d*f;QY3U&1lX&<{X5gvhY`GGrH)H9@k`I!eC-^1=&u&$>EsSo>eUtSe3L7Q|sc z?t>S3{;kCsHB_eaI0l?#-qmE`4Gf-rnrvNqMuuc+T;~G}JMe6o$je?Fn0b<96Gt zEZeTriL7Y?ccJb^!>?uN!Z<_q75o-6m=FkL72#54H*K&3Qcj^-3&67fpS?G2lH)ki zg#QW*pJudHXxs$XhFVJ0&d%&etCcj;jE447mR*(IT|iyRszP_uZ28;Y$0IT#+%r9{ z?CJ(UU=D&rW@SWp_7*;ss3nEYXYOc-pBgJ+FUEYlam~mBnu{>_Q3`6zXhYvuasfR zMd8`VfqQUkT!1D&7SmXmN)u6B)I6mdi`(tBj}t zIY!)4d@2TIp}b-;k2QGt^Tz#$-*)DpJieTuV3**m?MJ3OPtyoW%Hff3-gLJ}LqL!A zJ$i-5c-xq~3L6oX!mNl+px#3w$M!hr1J}n{jW^468%5vsPN| z|F7SiT^_gBtM2L5zubJc`q!ub@%8$v-~Gp5-~IO4KR>Fe)qe7>;ChOBu=gW>Drs3oZ)Y&th9c5#aXVu)Ovq@Q!A zK}P-;^2qaBr0tLhr2K*a3~()IjG!SK1EUwrw2P43fj9gFQnZwmjT&&B22HJqaU^4Y z0&LwHoFhL;@CM)+;dGcnXZqZU=ahu--gn)h4j#|NcD5TDJpkXcowtZ$Z>BHc`+qm* zL`;J#HTe6kxkd}xh1t||VJYAtnF1(P?*Tvo5s%Dxdfe=mwyljsNq6Fm0%2x;@$dDS z4+NSwXedN;d=c0|O)6Tylqiw`hoq+AZc4*gd3mS7Dl;Rszd=Ku$?>@a{2uk?wiBaP zD=8ttBVvmibkP7dz@QAoYHf%;l0RlBtln(QNC9j22L-wmv$)9izfs-914C1D1{e3Q zKHqNMZjUhrMzb0NMQ7kv$iHmE;V_iN>T+Qf5GlD$XGqjC1FkGQgJML_H~Yb3`Exuz z+}Ma8@iW0B*JM+}?~05cqiR?;o%p1dmyQaOTtK_;;1BYC*2&HWUY8-8cbCMyZghrr;ZQmuvdU7it-!>xX0?|zK0U+Q22Qd+pLY*s& zY%y<;Bo`Qb-8H)z9nd_M-3mNtGA6(YdPexzQmwH=s4zf#nsI4+ec((Q(tqiAlA_`f zKskDA;Czil#DG%;Jmk~FD@Vo}2&lNOij8^@#EqNJqvy+*euuq-GK&u z*mf_mb0>=XNErx5O~Ik7sGRjJ(IEa|o4kyn^|SBqGCnew_1YB;q<;oMTu0;YzIzH6 z&{NjV_#>9~JL-av_Jn{jOk@r9{wL)7F;^>@53kvTx&--A_c{0>cf{kP0~(3mS#?0v zFB)fQ=glDhMu`-|ae{OP% z_Uol;28E0KuiX~Q#N2~)H%#X#_vCfkYnXX6WQZi2qzjFz)VV9aO+H@vH=uL6nyjn$ z=|4aJ@$4^u+Whu>{Xg4Z&;Q*0>G+#}d9`}_FE{`GpNl{I;m6;;eYgJgA79S@dh!19 z*^mFb`?o)>*8lon|NQscfBxf}?SDS~Ki&2Jy}ErdfAKGWc)fh_`mK{!*lW8$ z&u&_RLJ=orMMI1*EJZjUB?;Hm8($h!@)TyT2K~?P2?_wJk0PTw)bI zF{H0Xb9=`ZXl@M~*kg(l0*Pg>kRWJ}k+?mkyA4RkZ3NOQW{&q*9j?mx!Q5|g2=)#P zU}M`#tcimvgguMSYCj)CQ^p4C52|59fE-SMwO!kw(dlY0nGBc`zHjK1VBhUy6V1sQ z3RpWuZu#L%vTqQMOgwqJ_K}J5VoJ*j`;AMFftv-TT_VLM1I&kKDZ-F`(31A`L2VL4 z5i$BAK?@90-~~Khz(~MemF-v}-WjtK&foxU_X5gKdmoF>>W z__&E^p{8I%1Iefg3ZY1T0!q)uB-ESg_5N>M;g(N=o<%MiI&=S--#G;X}Fu&@y>*dilRyR@Hm!aShuteH1 zthYsQ+Y81`#sj)S(YU2vy#d-u?l~9@V-z77WQiem3lyieOHK52zG(+8{U{hqLxZG;1ga>^WxW%T|z%|01m-KLN z;*;hy5;qeFU`{i;0Ods3FD(=HFg#NB%SexEZ%j>v1~2+}phAs{G6gP^CS*@y4MBY^ zZnV`LWJ#Ia2!J(*BejE%+b84;h^y%dSoLW^+O}n;PyvWMmStQvkIDvp&fi231KnBR z{qO=Lv$I0&``h#Rwmp0Q24H@VYFIzU3(d;>N7s=)-!0c&W2=$q3xNpn zg-G`&clUS$FoaBWEv3vp&?`o6__paxIwUyFD8_D{1;;RA>7+z3DYN61)S>2A+v|<3FPF?8r$hs_OQfb97nD)k zQw}dB!Ycycf?CG&XYhSt{4O^%I2+6N8Tlk51-DprM--x4*jVE^$R@14USh&C{%^Ol zsU0J-UcW;Ncv~|{y?Er0!Oy_dHhp`BRd6JV*Wj(iGRT44T&$LtSC)U!M#WnJi@>jt zEWlage&5;cQB|1u6ASFP`yHM)b_XK!qch7C^|nU#!N^ynlBkwemU$8_Yl){#-D&ucwol^8@qNYxy1^pnbk;sBuapCzFcZ%ij4*xPFZX@- z0;}~gAy8N|eTk`0W@4owI^qDYIZ9C=J3JmOk}jsEiBjRm^lc$CA*1>Z5-#1`RDO$h zfVgzb28_jQaI}NtMy_ICaJcK>kxLH-xzOg^&q{7rVm*gb?9#)*CR}>yJw@E}ql*fM zeRvqfLY?w*Cjbd`Na$yB-ZS|TlMbb{Q~OQd8Bh2QH9V1fo&W-VMyonHq<*k__GgqN z5_YB^oF`*WI5<`fV_}Vj9Q=I`cJ%Za2WdY8`_^wV9-#Or&L}yqc9(aiW6n*f$p&OG z7fK%kGhmB|mzA@abJFlBqzKuVs% zI|*0jKKnrWLmnbAp(!soiccAjP=#Pa(^nX<@m!t?Q@c>%gw!3x7JIa-FG7h0? z!lB!tb#@;>Uv|!M27B5iXBbK0x&GvA?!X9_HCmrx2hd%DBYk4ds5t(Dt*YA%`ZuaZH&V2kj!uWOv^l7HKm*h6pESc7Z+;tJPY(Fd# z4iB_yFPk|<_#8#>*cXvih+KZ$C+b>+Op%>P0ZGJPmT`msQ#ey9bq6HDnceA#qDaxc z+P+&Znp7u`;NBy-9IO5cWRTI0sB0Al-%yth$9uFsn7ihIP1*2RrH+;f2xHs)$<{io~2(K^{;2CeFL*F$0@XrRL zyauHj-&%+qmzWX=PtxJ&n+%bn9S9oVvlBA9*3D3+vO@8K({!x=Ngz2*G23JGLUSFO zYl;_KJ1Y7u;fKR6th?Bv=@_|@9{=!0FhD%R1C*VaX9Oi zWt0K)7MTPG=tgEV>iH2le>O@yVhWU7Vv>}=52&jw1KGOWUiSbSY;l8@d9*)!g5edL zbmL}E-DYiK?|{)D`~0HC5Da=wNj-wcm>*+g$R%b5pC4EDBh*weC=!4X8y+ovh4c*l z+&r75UyA#rvCsVs=J79A$IkFT{nR+}C8!1%h_=d&MT^npvO#JzlTl#IaQ+d4b+1|@ zOVQ$}44dIp^g?L1L-#Gfc3AJ|N+_d>Ha`*|*l|012l>`t$^a?{YJk6RxF)?HSe#}R z;04S)LAkfZIH_9GJ4d545qS+r5BZfFQ?zWy@BLu?*N%5PAW)hhHs|;$zG#^WZ5WTv zY)8trqf|oo8~;*YL{{?N3`GgrH{E_xvIGDowlAGv(t2|H=X&s$o^`1rJb!2$K z;Yyjjfw)g2X8pp1BgS?W+>1M2Wqt#25i>jI?`FsdG{3bfmP0Ftvbof(5I*t02&%3_ zF03_h>}k%0%63>@tC8sKF3OB}_xajB%LX`S z=AZwgZ$tJ?Nxgdv*4q`DJ5W2))+E$xFeixb9Ge4d#!O zVpz)T3nLv5BjhlXIrRqm?ap8_8Gk$z2JSw<#W3Hg?^0s(?}Hp87H2l+0vNVyq&TD1 z22t~axq6%S+P7%5Tdk$s6EH$9Yt9TxoPr-ROG!g7*iOXv9aK-l3&+(J0IFXDmy5+^XogWK>fl9Ca-0gIv0$gK=sKv+ttoYq%=+S z$>43cEze6ntkNCK3=Iy`?a5CL4%P*w0RfKWlN*2E8h7Z~8r}ZkNwJPMtRnfpw-B9a zN~FRKQOJk|93@DbA$NM8P-K%hg&;wAZ~+(HTk3{k8u^iC$Eg}0a$CXtBIErfWkO~* z;wA38pTC38(9G8}dI$Mh#G>TGpD`zxZGz{}9v|z1A87Pr_@LHK->|CF_!XLKz?w=A zr&#O7wjrO*?IdR>Gd|;g@?mM#crKk|Q^Q#4>pYpTdHLkVcEYflu2cT1xo;GUw6oB? z2aPmy7STq}-&Q=-F_wVj2H?COcnV=bk&v04v~G}{hHMCAA2?%$3)uvDsUIHc>bu6$ z4a=Q~xhXR&l;LIl4M?j&gS!Mn2JVHCrGWuT48tLjzpTuug8;2QG+SLeFd4-6Ec5HP z%pwfho*@h-il!WxQLQ?s$YUROltw`M7Qz#e z`^LmOrhA>{zM1pJDu55Fm<)}b*mZww+6j;4YkQUkZOu((*a^)qn=Uc)6AEBT7Q^~e zoKO%63igQ=+EvO_i<+w(X#y^NV*$Sh&G$Srf8n@{`BN{p(6vGrk00tQ2$Y}C`D~hgZf@{YL75dn7J=lvQq{; ztoFelH7-ed{}vsT)Z$<8uts@~KP>Ms-i~Il?U>B+>SnxrnVHwU}~9;Tm$6 z=CN`_fPRNWe5%ng)v_&b5J$|5df~2-9SR)_wHg*wXz*uqhjszT?zkZXFKs7Z z!8aI2>=taqBW{NElMD`OkN?%KZM)w@bJ=a{9!jeuK0J1A9uF6@H`E6VwGw^8rOiwp zp}=sR-?axmS^+2|)^eaRB@iy>9($nT`u^Lfb#SM^uSYv6`-0nN_BxRVP2-Fxvp+(1 zKp^gFW-eO6k2BW^CPW+j0YNCNX^-64uor&C z{qqR~3s93RdwduT`{eN^fRvOVsFYM$(hn@60|y$sp9510V+>2EWQf_113r?1WQ%;U zc;^b5!@>ylG@eNey9QhA2dn*rE|n8N_XvJ*k6^j;0YT?fY@s0o#WSjix%6^FG$XgY zqLTLeaK7~>Q@_694=+(~{UTrzsC&_mRHw*%Jx#xPLTaUf0F%9shmxIL4whsaD(Vx! zhDFD&I2@@!wAEj+|HV|bzBVHaM@A1~B$F}7aS1DvEo#mj7rLkq0>49}8@I5_)*W4M zMqdf}*D2CiqRQ-i)xLNC1u`jvto}DbA#Z(GVz}$>2%~ zU_CRbP!NDTJrtydAi^vfbck@KK_`h6zx?I|n}^&cOEgHQAEUa49`$ zgxW`wm{ZGePJ&z6peHFH(+DGuPJ$YzN|PGGI>th}lqy)P=ob3z2N+7O5Uy*EFJH!$ z@%*|&FSI@oO$F?w<`D`oB20nU8zYH#j9|+fNSXOY*WDlMCekk(13G$japg@MrZNX^0+bsz=24Lsjxu}ZiFWxtWcxe zos2EVQ}-)_2e}l zJTr9Fr0#jOYIbN=h#)AU9|q`=H3+W?9Bis#_dxy8>{Q8_oTsHPPFLMa;s zWbHY^a>KZ6W~1#P+K(IMQpjZ4qkqfAGS(}xN~RMc<<>-}uqe(7 z7F=@{REJXXH$)<^E&hERCVKnHeFH?8dj^+UWb5D_Po6Q%O4D1ROKrVG62iiC_clu0 zOKktqKRpU1z!k_=q)~6z_ME2bOsAi(%1yn(Q#SK?yE_GdE~$!Q#U-7LXE`*In|Al1ZCYMYMgWUV$C>ro+%Dlo;LinA zzTL=eo6qQb=H9DA|r4Q$C7 zI7tlgB6&7s=^S82qNNu#){m-5H2XF1s5&M^nb_{u~gd;aVb^KuR_u&G;9fv24yosNekr^(2%)U!tbaR z9R{L{q0%MjK7gE*4gS13G=-m)-BwP#V@;7bs(l?MQo=~`%p!xb_zEcqC4fLimkF|I zn?bkZ=j;X_qiXeENk>_&{^S~~7mcn9a?-7Wn{&|12Cwd}`Sr z>qST?5IFj2G@U+s8Iwa0=cUG#M&yk8r7YKGPQV46-=-@+QmlCXBZzPCh0!zu6JK!5 z%G5IjEdxKlZPu>yyx_SW;WMndETF%e^#c*Ki>sorLdWB{S18XM!nxNp2*L(JT#Af4 zGM;XbEQ}Zv#65zRCIR2Lfgr!R;Y!f=1yYnCMYgo#56~gDqyUYgW0hF10VsJ@u4iw?_0drH>cg*N~-b~prMMqlGD&sF9`g_Qjxa67vn!?MR0WUB2)%0 z7Vu)^gTH9@2l-e;=)}4~d;)FI=^FJCUMz`cJBQse2@<~wlT>8X$6stlo)kI9hL^TW zgl(qB;^I1SbBS>?m(-%rNC*QXyUzV^{{fCV7;7jhB~%THp5kH8;p(USH4n{w*k9hA2 z_ko=53yb4f;09O@9bXBPfBB7&Q8cE?{&hHC!CgqfF2VIsmt?H3Pho_J8L$GRkU(SG z*C0l!W+0s!w9W{dDw>m9cC91(1}F^oxmRbpn2ZyoTRHSWyhoitzpvpI^h@KG!^d%H zuG^QkU%ING?`HNZ9B}cg`Xri?WoI6d4IZKp8ORjsR~ssY4I@Cgwg&#p_<=1<3e=CM z%sqg@2Wdb$%nW2navgWerHw-a`{?UxU&>2(`<(`!6A)%t>40^#YeW$v9OKm!SZ02+ z8DI-E97*5+r{D)J+V?G5cI2}wz?C^%hn`R`oa5vXl29;xm6TVKz9t?fu8X}!bCJwT zBM)Y;%=I>b?WiQalGXOy3sxmn)Iq8$Kx%nmW?4YU< z&L7XA^Fz6tcYwcs$PNR*wriIXYDq&J(!Ky6igUKoA`VskjU=UFQ8NkJ;&x_N>r;QQ z;}}~FTg>3IU>?a`{_>k55`?yFiyELCU2nTZJU>YYD`tEN63c99fGIB_8@lOV;!aya>V=iAb!DiQ&X2f-h+u7LY5sfr4MsqlG zLe>Ir_w5R~m=UU~nwV5OuH<~XMarigygU|PL{rySeIEqUr36WVpI?EiuRMwj$Q93B z=QHB>JQoVrv7sIpSFLH>;$P%~UcpN)Bp4D+K5fw^#Iu1bq^NaPUi?xW7?NA02=G>?M&+6#*N zzhx^nv=bLz>_|h!jrFpKG}QjT9Q^&sGQcR5x?6JjXq$)a!F|Q)%nOpiNyOM6ZWFa* z#KHZ!@++}dW5Z(sG;^}Y=n+Rv5Nn}Vs79G%Ni)W+ti$vL&xp#OGzrs08Y{Vm7)S@& zQsS$}u0x9V>J||%%YPRM^ava~L(M`(b1kg!VUhn8SKVw34!tH9X5Ir;YFhpk9FiU- zGE@OEL+sI^Ljn@FXbNm{uC$9JCd*D=4BrI;9rXVZpu(RAW$47DfG(kwQM_9nT^NHy zpayUSU_)D8C~n;Jtki6Lg50{s3G=yBed8c`Fx5o$L{qwC8V)sZ?K4li+T17)Fq zsh9jeK$Tz!;+N0hRZ$|22ae zL5S2jf`_Bv(hDkK=#j%hbzd%4lq*dWj8XLxL+<<6aKOAus22hjf@1TuLc#DppFy<8$?W3gs`4seYevDh?|HV-+Vbwc*sN5KDeMP8h!I&8sNdKjM?cD@e8iq-%Gune!e44aXlwu9^G zJ_k>Z8%(l5EYYT4TRU+?LnBvNAIOk!cns5hNSuc6_;Y}uLtz3%Xg z3uMd~nY*G&Zc-ED+rjI6*;+RVSxX8t%HUyq*I4<7&P{R8ybBV`KC3< z&@lp=oB$avDW}y2o$S=c_wae0xa4{RmQJ|dC^9KwJgXWv66uuphA4Bdx|3iJl2F1v zG|C`k4ldm&=rU}v0=Z*$4kaltW%eXSb+-7(mqR(vmt*PHyfOKJ^J`N5Y?M3j5cUH4 znMdDor8~;c<3&cIqXN<|ylLWgmrFb^(=JvLPdqGiG~wFhY2>$7`4un#0A|Ho97*w{ z7v_k(@WedvV-H@bAK}Nl<%U{;Ky_FnQI_3ptuj!9^%|_@-}M{Tpf5uzHPAmH-41q} zs7bRbu}9VlQqF9*5U*teT@9dx`aoto6u}Rwf6A6znR2>VSYxo8JiJm%?P7+iq=OS8 z5pUUOn@6bwyP=XV=qqchdtoNIwvu)SQ4f#)DsUxq} z28RukvDfUFOmgpNZ5ONOzBebixkd*Ru*tqyiPn4b^yyQ|i?dCFsR?e$DKH@n=hn!N zQ1R5(V27EvXmDy9H}>$RZbWXU?qs@~&_%CDt>A{4hg!ng^>&L`lV2%yvj=M1m}nMJ z%?kXnvb?G=FJsY~J#O0T6^yUQ1xn{qhEjz&pV?-a&%y369nlY}TEX_RS4RCVXG!k{ zTW`QnvLDCPk$24{P2Jq!k63~>5lu076fln){K3gZ9jNO~`DekXsXe7sC%%*~K1zyC zgYg4Akq~rOsqtHTxt>+z6%6)jGyzc5Z}l1VJ{UpTduzC{ot0omIRzTQ^Q$B9-8SSi z*-T230!(e&oY(Mksleb1CR~tw12SwzJ?O}gGvA>4oj^qR1_F);WG|VAqb}@ZT{J7q zBwB_{{$MQQ*CcJI-wC}HIHoi93`y_^RfhSoz@)!`TGt|S_|Qyg+g%;^X9gwyN8dZ- zn?T)nBUN#)8n($?^8)Vc5BmV{L4+C5scxjwOT{Dwa%|P$GM#~E;1YzUhh#0<+#^^9 zG^jyFQe_Rs#6_E^<+O?7we%R^dN+z;ThUHW%2ePT1L$c?ZW5h4f ztsKH_AND4P`_bTFcjzgxLfAggb-FTSZS!=A%#)tpyr@x(U*e}wjqdpSwu@y9(&U(+ zznliWn*|B$5U>nf0J?=6WPbx`Rffu1_>mkon$KEY(w=nmW-X6=L_zo( z)dK5E#nqitM@K$a=QKcfDV02jF3%rF+kS|lEmZl9`a*P2-q^X6DvVxF9=4ugIFC#N z84yECiDwINey@=i6d+KPu|D1Lx*^%xv zC~6c2=aQP(bTD|n@<)>d$ye7-JYB%WZ`Ma^rpfm^95z0RWcoQXbXBz54NB0HX`v#c zAuXuM8vLB zRjE{ke!G6U6^#A%W=kVSUH(AQ@mY;nUvB$7z$oe^2X0)y$@mHBd?sfQMX^Ohj(yTJBm|3o zjQV|PhG|d?CrC|{@}oroC3}03LO2<|Nm}8s9RLRop5w=uN7Q$opkIe;@NlX0IRJBT zbj%Px!{Q7Kbq*{1^txG5YhaDYaZ25sNf7gPO%v+-=0B0|4uWtoOv%Ur9s~o7eRn|G zvm=e_mw*Wj4@iT zHiX;25u-qLkg@NCJ0??01^Pq?8ca5A_&&gMz%&_N zFz?VY#pN&#j}#DX|8HtWH|jK$p=%a+%Iy)supRmaMYc*2G@%Qolq#{+#Jb1b3K2L{ zx1xauvrr@aq)?e|(e`2sKZ~2|?sRE}@acpfv>;N3#d19wNMS#ZX2Q!0<}H)}M>H;uw(pY^ENg?am&gYoDbIu!NbGZ^#jveAp*lNEVbp_&_fbbs^Jv%n?+_ae}It5DkmtCS$jDYT4 z93iHWTZhr@Xe&0qqV(Wnta+0$%3f7PN)q=(>7khWx<_X6CSeD1n=PZ>3CzlHe|(nP zbE$p$2?QFR(@cUO2PYtpaPK~Q^0jv=0DWMOkps8aSq8X8;|*-Or^36h&{%c0=+3D} zgMA!`5g#70_HYC|P33Zm&xjXIE7dLQuSjWqB|(yQy*$8W5zGCgy7OY3t3d�C?iB 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 diff --git a/pkg/landscape/lib/gladio.hoon b/pkg/landscape/lib/gladio.hoon index a6d59a85e..f5151e1cf 100644 --- a/pkg/landscape/lib/gladio.hoon +++ b/pkg/landscape/lib/gladio.hoon @@ -6,6 +6,24 @@ /- *group |_ =bowl:gall +$ card card:agent:gall +++ import-club + |= [=^groups =associations:met =network:gra] + %- ~(gas by *imports:club:i) + %+ murn ~(tap by graphs.network) + |= [=flag:i graph=graph:gra mar=(unit mark)] + ^- (unit [flag:i import:club:i]) + ?. =(mar `%graph-validator-chat) + ~ + ?~ assoc=(~(get by associations) [%graph flag]) + ~& missing-assoc-club/flag + ~ + ?~ group=(~(get by groups) group.u.assoc) + ~& missing-group/[flag group.u.assoc] + ~ + ?. hidden.u.group + ~ + `[flag members.u.group u.assoc graph] +:: ++ import-for-mark |= [her=ship =^groups =associations:met =network:gra] |= =mark @@ -23,6 +41,8 @@ ?~ group=(~(get by groups) group.u.assoc) ~& missing-group/[flag group.u.assoc] ~ + ?: hidden.u.group + ~ =/ writers=(set ship) (~(get ju tags.u.group) %graph flag %writers) ?~ log=(~(get by update-logs.network) flag) @@ -58,6 +78,7 @@ ~& ships/ships =/ dms (~(get by graphs:network) [our.bowl %dm-inbox]) =/ import (import-for-mark our.bowl groups associations network) + =/ clubs (import-club groups associations network) =/ chats=imports:graph:i (import %graph-validator-chat) =/ diarys=imports:graph:i @@ -69,6 +90,8 @@ %+ murn ~(tap by groups) |= [=flag:i =group] ^- (unit [_flag import:groups:i]) + ?: hidden.group + ~ ?~ assoc=(~(get by associations) [%groups flag]) ~& missing-group-assoc/flag ~ @@ -100,6 +123,7 @@ %- welp :_ (migrate-ship our.bowl) :* (poke-our %groups group-import+!>(imports)) + (poke-our %chat club-imports+!>(clubs)) ?~ dms ~ (poke-our %chat dm-imports+!>(p.u.dms))^~ == diff --git a/pkg/landscape/sur/migrate.hoon b/pkg/landscape/sur/migrate.hoon index 3d5904229..3bf567298 100644 --- a/pkg/landscape/sur/migrate.hoon +++ b/pkg/landscape/sur/migrate.hoon @@ -14,5 +14,10 @@ +$ import [=association:met chans=(map flag =association:met) roles=(set flag) =group] +$ imports (map flag import) -- +++ club + |% + +$ import [ships=(set ship) =association:met =graph:gra] + +$ imports (map flag import) + -- -- From e41e5893674fdbfaae3f49e8281404cff1371a36 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 24 Nov 2022 14:21:17 +1000 Subject: [PATCH 021/136] landscape: add backups to groups 2 migration --- pkg/landscape/app/graph-store.hoon | 12 +++++++++ pkg/landscape/app/group-store.hoon | 21 ++++++++++++--- pkg/landscape/lib/gladio.hoon | 42 +++++++++++++++++++++++++----- pkg/landscape/ted/keep.hoon | 14 ++++++++++ 4 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 pkg/landscape/ted/keep.hoon diff --git a/pkg/landscape/app/graph-store.hoon b/pkg/landscape/app/graph-store.hoon index 2e5db90fd..b62fd7763 100644 --- a/pkg/landscape/app/graph-store.hoon +++ b/pkg/landscape/app/graph-store.hoon @@ -129,9 +129,21 @@ ?+ mark (on-poke:def mark vase) %graph-update-3 (graph-update !<(update:store vase)) %import (poke-import q.vase) + %migrated (poke-migrated !<(resource:store vase)) == [cards this] :: + ++ poke-migrated + |= r=resource:res + ^- (quip card _state) + =/ =path /(rap 3 'backup-' (scot %p entity.r) '-' name.r ~)/noun + =/ graph (~(got by graphs) r) + :- [%pass /migrate %agent [our.bowl %hood] %poke drum-put+!>([path (jam graph)])]~ + %_ state + graphs (~(del by graphs) r) + update-logs (~(del by update-logs) r) + == + :: ++ graph-update |= =update:store ^- (quip card _state) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index e4c16ac71..cd711aa11 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -91,8 +91,9 @@ cards %- welp :_ cards - :~ [%pass / %agent [our dap]:bowl %poke noun+!>(%migrate)] - [%pass / %agent [our %hood]:bowl %poke %kiln-install !>([%new-groups ~zod %groups])] + :~ [%pass / %agent [our dap]:bowl %poke noun+!>(%export)] + [%pass / %agent [our dap]:bowl %poke noun+!>(%migrate)] + :: [%pass / %agent [our %hood]:bowl %poke %kiln-install !>([%new-groups ~zod %groups])] == == :: @@ -132,7 +133,12 @@ =^ cards state ?+ mark (on-poke:def mark vase) %sane (poke-sane:gc !<(?(%check %fix) vase)) - %noun ?>(=(q.vase %migrate) poke-migrate:gc) + :: + %noun + ?+ q.vase !! + %migrate poke-migrate:gc + %export poke-export:gc + == :: ?(%group-update-0 %group-action) (poke-group-update:gc !<(update:store vase)) @@ -219,9 +225,16 @@ :: |_ bol=bowl:gall +* io ~(. agentio bol) +++ poke-export + ^- (quip card _state) + :_ state + =; =(fyrd:khan cage) + [%pass /export %arvo %k %fard fyrd]~ + [q.byk.bol %keep noun+!>(~(export gladio bol))] +:: ++ poke-migrate ^- (quip card _state) - =^ cards-1 wait + =^ cards-1=(list card) wait ~(migrate-start gladio bol) =/ cards-2=(list card) %+ turn ~(tap in wait) diff --git a/pkg/landscape/lib/gladio.hoon b/pkg/landscape/lib/gladio.hoon index f5151e1cf..13b42759a 100644 --- a/pkg/landscape/lib/gladio.hoon +++ b/pkg/landscape/lib/gladio.hoon @@ -57,10 +57,24 @@ /gx/(scot %p our.bowl)/[dude]/(scot %da now.bowl) ++ groups ~+ .^([@ =^groups *] (scry %group-store /export/noun)) +++ groups-raw + .^(* (scry %group-store /export/noun)) ++ network ~+ .^([@ =network:gra] (scry %graph-store /export/noun)) +++ network-raw + .^(* (scry %graph-store /export/noun)) ++ associations - ~+ .^(=associations:met (scry %metadata-store /associations/noun)) + ~+ .^([@ =associations:met ~] (scry %metadata-store /export/noun)) +++ associations-raw + .^(* (scry %metadata-store /export/noun)) +++ export + ~> %bout.[1 %export-jam] + %- jam + ^- * + ~> %bout.[1 %export-scry] + :~ [%group-store groups-raw] + [%metadata-store associations-raw] + == ++ peers |= =network:gra =- (~(del in -) our.bowl) @@ -72,10 +86,9 @@ ++ migrate-start ^- (quip card (set ship)) =+ network - =+ groups =+ associations + =+ groups =/ ships (peers network) - ~& ships/ships =/ dms (~(get by graphs:network) [our.bowl %dm-inbox]) =/ import (import-for-mark our.bowl groups associations network) =/ clubs (import-club groups associations network) @@ -119,9 +132,15 @@ ~ `[flag u.assoc chans roles group] =/ dms (~(get by graphs:network) [our.bowl %dm-inbox]) - :_ (peers network) - %- welp - :_ (migrate-ship our.bowl) + :_ ~ + %+ welp + ^- (list card) + %- zing + %+ turn ~(tap in (~(put in ships) our.bowl)) + |= =ship + (migrate-ship ship) + ^- (list card) + :: :* (poke-our %groups group-import+!>(imports)) (poke-our %chat club-imports+!>(clubs)) ?~ dms ~ @@ -141,8 +160,17 @@ (import %graph-validator-publish) =/ links=imports:graph:i (import %graph-validator-link) + =/ graph-flags + %. ~(key by links) + =- ~(uni in -) + (~(uni in ~(key by chats)) ~(key by diarys)) + %+ welp + %+ turn ~(tap in graph-flags) + |= =flag:i + ^- card + (poke-our %graph-store migrated+!>(flag)) :~ (poke-our %chat graph-imports+!>(chats)) (poke-our %diary graph-imports+!>(diarys)) (poke-our %heap graph-imports+!>(links)) - == + == -- diff --git a/pkg/landscape/ted/keep.hoon b/pkg/landscape/ted/keep.hoon new file mode 100644 index 000000000..3eb758cae --- /dev/null +++ b/pkg/landscape/ted/keep.hoon @@ -0,0 +1,14 @@ +/- spider, docket +/+ strandio +=, strand=strand:spider +^- thread:spider +|= arg=vase +=/ m (strand ,vase) +^- form:m +=+ !<(jim=@ arg) +;< =bowl:spider bind:m get-bowl:strandio +=/ home=path /(scot %p our.bowl)/[q.byk.bowl]/(scot %da now.bowl) +=/ =path /(cat 3 'backup-' (scot %da now.bowl))/noun + :: XX remove prints +;< ~ bind:m (poke-our:strandio %hood drum-put+!>([path jim])) +(pure:m *vase) From a64881ccb2340c868a9fb53fe4d3e50ca4105bcb Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 24 Nov 2022 14:41:07 +1000 Subject: [PATCH 022/136] graph-store: drop archives and backup --- pkg/landscape/app/graph-store.hoon | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/pkg/landscape/app/graph-store.hoon b/pkg/landscape/app/graph-store.hoon index 2e5db90fd..fe7a5d1e3 100644 --- a/pkg/landscape/app/graph-store.hoon +++ b/pkg/landscape/app/graph-store.hoon @@ -11,10 +11,10 @@ [%3 network:one:store] [%4 network:store] [%5 network:store] - state-6 + [%6 network:store] == ::- -+$ state-6 [%6 network:store] ++$ state-7 [%7 network:store] ++ orm orm:store ++ orm-log orm-log:store ++ mar %graph-update-3 @@ -96,7 +96,23 @@ (scag 2 (tap:orm-log update-log)) == :: - %6 [cards this(state old)] + %6 + %_ $ + -.old %7 + archive.old ~ + cards + ;: welp + cards + :: + %+ turn ~(tap by archive.old) + |= [r=resource:store m=marked-graph:store] + ^- card + =/ pax /(rap 3 'archive-'(scot %p entity.r) '-' name.r)/noun + =/ =cage drum-put+!>([pax (jam m)]) + [%pass /archive %agent [our.bowl %hood] %poke cage] + == + :: + %7 [cards this(state old)] == :: ++ on-watch From ab578c7018610e25c347aa452dbdca598a187bd7 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 24 Nov 2022 14:52:03 +1000 Subject: [PATCH 023/136] graph-store: nuke groups desk and uninstall --- pkg/landscape/app/graph-store.hoon | 9 +++++++-- pkg/landscape/lib/graph-store.hoon | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pkg/landscape/app/graph-store.hoon b/pkg/landscape/app/graph-store.hoon index fe7a5d1e3..45832db08 100644 --- a/pkg/landscape/app/graph-store.hoon +++ b/pkg/landscape/app/graph-store.hoon @@ -12,6 +12,7 @@ [%4 network:store] [%5 network:store] [%6 network:store] + state-7 == ::- +$ state-7 [%7 network:store] @@ -20,7 +21,7 @@ ++ mar %graph-update-3 -- :: -=| state-6 +=| state-7 =* state - :: %- agent:dbug @@ -104,12 +105,16 @@ ;: welp cards :: + (nuke-groups:upgrade:store bowl) + :: + ^- (list card) %+ turn ~(tap by archive.old) |= [r=resource:store m=marked-graph:store] ^- card - =/ pax /(rap 3 'archive-'(scot %p entity.r) '-' name.r)/noun + =/ pax /(rap 3 'archive-' (scot %p entity.r) '-' name.r ~)/noun =/ =cage drum-put+!>([pax (jam m)]) [%pass /archive %agent [our.bowl %hood] %poke cage] + == == :: %7 [cards this(state old)] diff --git a/pkg/landscape/lib/graph-store.hoon b/pkg/landscape/lib/graph-store.hoon index df11d0349..4fb283748 100644 --- a/pkg/landscape/lib/graph-store.hoon +++ b/pkg/landscape/lib/graph-store.hoon @@ -528,6 +528,21 @@ :: ++ upgrade |% + ++ nuke-groups + |= =bowl:gall + |^ ^- (list card:agent:gall) + ?. .^(? (gall-scry %u %groups)) + ~ + =+ .^(=desk (gall-scry %d %groups)) + :~ [%pass /nuke %agent [our.bowl %hood] %poke kiln-nuke+!>([desk &])] + [%pass /nuke %agent [our.bowl %hood] %poke kiln-uninstall+!>(desk)] + == + :: + ++ gall-scry + |= [=care:clay dap=dude:gall] + ^- path + /(cat 3 %g care)/(scot %p our.bowl)/[dap]/(scot %da now.bowl) + -- :: :: +two :: @@ -758,9 +773,9 @@ -- ++ import |= [arc=* our=ship] - ^- (quip card:agent:gall [%6 network]) + ^- (quip card:agent:gall [%7 network]) |^ - =/ sty [%6 (remake-network ;;(tree-network +.arc))] + =/ sty [%7 (remake-network ;;(tree-network +.arc))] :_ sty %+ turn ~(tap by graphs.sty) |= [rid=resource =marked-graph] From d9ae7c307b3d9dd2dec76fcf184972a510a5d7a4 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 24 Nov 2022 14:55:02 +1000 Subject: [PATCH 024/136] graph-store: standardise archive format --- pkg/landscape/app/graph-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/app/graph-store.hoon b/pkg/landscape/app/graph-store.hoon index 45832db08..f7197d08f 100644 --- a/pkg/landscape/app/graph-store.hoon +++ b/pkg/landscape/app/graph-store.hoon @@ -112,7 +112,7 @@ |= [r=resource:store m=marked-graph:store] ^- card =/ pax /(rap 3 'archive-' (scot %p entity.r) '-' name.r ~)/noun - =/ =cage drum-put+!>([pax (jam m)]) + =/ =cage drum-put+!>([pax (jam r m)]) [%pass /archive %agent [our.bowl %hood] %poke cage] == == From b76ea6516cb7de8ffe47b55503bf4a3a26aecf00 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 24 Nov 2022 15:12:21 +1000 Subject: [PATCH 025/136] graph-store: add thread for recovering archives --- pkg/landscape/ted/graph/recover-archive.hoon | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 pkg/landscape/ted/graph/recover-archive.hoon diff --git a/pkg/landscape/ted/graph/recover-archive.hoon b/pkg/landscape/ted/graph/recover-archive.hoon new file mode 100644 index 000000000..455ee9feb --- /dev/null +++ b/pkg/landscape/ted/graph/recover-archive.hoon @@ -0,0 +1,19 @@ +/- spider, graph=graph-store, met=metadata-store, *group, group-store, push-hook +/+ strandio, resource, graph-view +=> +|% +++ strand strand:spider +++ poke poke:strandio +++ poke-our poke-our:strandio +-- +=, strand=strand:spider +^- thread:spider +|= arg=vase +=/ m (strand ,vase) +^- form:m +=+ !<([~ pax=path] arg) +;< =bowl:spider bind:m get-bowl:strandio +=+ .^([rid=resource mar=marked-graph:graph] %cx pax) +;< ~ bind:m + (poke-our:strandio %graph-store %graph-update-3 !>([now.bowl %add-graph p.mar q.mar &])) +(pure:m *vase) From 0f7f4d554df5d6fc4c3cf19020eaeb1664975b0b Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 24 Nov 2022 15:16:17 +1000 Subject: [PATCH 026/136] graph-store: use standard archive format --- pkg/landscape/app/graph-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/app/graph-store.hoon b/pkg/landscape/app/graph-store.hoon index b62fd7763..aad097514 100644 --- a/pkg/landscape/app/graph-store.hoon +++ b/pkg/landscape/app/graph-store.hoon @@ -138,7 +138,7 @@ ^- (quip card _state) =/ =path /(rap 3 'backup-' (scot %p entity.r) '-' name.r ~)/noun =/ graph (~(got by graphs) r) - :- [%pass /migrate %agent [our.bowl %hood] %poke drum-put+!>([path (jam graph)])]~ + :- [%pass /migrate %agent [our.bowl %hood] %poke drum-put+!>([path (jam r graph)])]~ %_ state graphs (~(del by graphs) r) update-logs (~(del by update-logs) r) From 9d0877c2190cefe0462fad24228770972c81861c Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 24 Nov 2022 15:42:50 +1000 Subject: [PATCH 027/136] landscape: support pending state in migration --- pkg/landscape/lib/gladio.hoon | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/pkg/landscape/lib/gladio.hoon b/pkg/landscape/lib/gladio.hoon index 13b42759a..90a4f9e54 100644 --- a/pkg/landscape/lib/gladio.hoon +++ b/pkg/landscape/lib/gladio.hoon @@ -24,14 +24,20 @@ ~ `[flag members.u.group u.assoc graph] :: +++ import-flags + |= [=^groups =associations:met =network:gra] + |= =mark + ^- (set flag:i) + ~(key by ((import-for-mark ~ groups associations network) mark)) +:: ++ import-for-mark - |= [her=ship =^groups =associations:met =network:gra] + |= [her=(unit ship) =^groups =associations:met =network:gra] |= =mark ^- imports:graph:i %- ~(gas by *imports:graph:i) %+ murn ~(tap by graphs.network) |= [=flag:i graph=graph:gra mar=(unit ^mark)] - ?. =(p.flag her) + ?. |(=(`p.flag her) =(her ~)) ~ ?. =(mar `mark) :: XX: correct detection? ~ @@ -90,7 +96,7 @@ =+ groups =/ ships (peers network) =/ dms (~(get by graphs:network) [our.bowl %dm-inbox]) - =/ import (import-for-mark our.bowl groups associations network) + =/ import (import-for-mark `our.bowl groups associations network) =/ clubs (import-club groups associations network) =/ chats=imports:graph:i (import %graph-validator-chat) @@ -132,16 +138,16 @@ ~ `[flag u.assoc chans roles group] =/ dms (~(get by graphs:network) [our.bowl %dm-inbox]) - :_ ~ - %+ welp - ^- (list card) - %- zing - %+ turn ~(tap in (~(put in ships) our.bowl)) - |= =ship - (migrate-ship ship) - ^- (list card) - :: + =/ flag-importer (import-flags groups associations network) + =+ :* chat-flags=(flag-importer %graph-validator-chat) + heap-flags=(flag-importer %graph-validator-link) + diary-flags=(flag-importer %graph-validator-publish) + == + :_ ships :* (poke-our %groups group-import+!>(imports)) + (poke-our %chat import-flags+!>(chat-flags)) + (poke-our %heap import-flags+!>(heap-flags)) + (poke-our %diary import-flags+!>(diary-flags)) (poke-our %chat club-imports+!>(clubs)) ?~ dms ~ (poke-our %chat dm-imports+!>(p.u.dms))^~ @@ -153,7 +159,7 @@ =+ groups =+ network =+ associations - =/ import (import-for-mark her groups associations network) + =/ import (import-for-mark `her groups associations network) =/ chats=imports:graph:i (import %graph-validator-chat) =/ diarys=imports:graph:i From 8f6366d1c977a31dc33b624474491b5772e6a6e3 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 25 Nov 2022 12:32:13 +1000 Subject: [PATCH 028/136] landscape: remove testing cruft --- pkg/landscape/app/group-store.hoon | 2 +- pkg/landscape/lib/gladio.hoon | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index cd711aa11..f95dfccd9 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -93,7 +93,7 @@ :_ cards :~ [%pass / %agent [our dap]:bowl %poke noun+!>(%export)] [%pass / %agent [our dap]:bowl %poke noun+!>(%migrate)] - :: [%pass / %agent [our %hood]:bowl %poke %kiln-install !>([%new-groups ~zod %groups])] + [%pass / %agent [our %hood]:bowl %poke %kiln-install !>([%groups ~zod %groups])] == == :: diff --git a/pkg/landscape/lib/gladio.hoon b/pkg/landscape/lib/gladio.hoon index 90a4f9e54..67bf9ab73 100644 --- a/pkg/landscape/lib/gladio.hoon +++ b/pkg/landscape/lib/gladio.hoon @@ -144,6 +144,7 @@ diary-flags=(flag-importer %graph-validator-publish) == :_ ships + %+ welp (migrate-ship our.bowl) :* (poke-our %groups group-import+!>(imports)) (poke-our %chat import-flags+!>(chat-flags)) (poke-our %heap import-flags+!>(heap-flags)) From ce1332565e98ce6f824230f5dab5172ae3a78aa9 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 25 Nov 2022 18:14:53 +1000 Subject: [PATCH 029/136] graph-store: drop signatures --- pkg/landscape/app/graph-store.hoon | 32 ++++++++++++---- pkg/landscape/lib/graph-store.hoon | 59 ++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/pkg/landscape/app/graph-store.hoon b/pkg/landscape/app/graph-store.hoon index f7197d08f..8c4607070 100644 --- a/pkg/landscape/app/graph-store.hoon +++ b/pkg/landscape/app/graph-store.hoon @@ -98,22 +98,38 @@ == :: %6 - %_ $ + =/ old-dms + %- ~(gas by *(map resource:store marked-graph:store)) + %+ skim ~(tap by graphs.old) + |=([r=resource:store *] (is-old-dm:upgrade:store r)) + =/ backup (backup:upgrade:store bowl) + %_ $ -.old %7 archive.old ~ + update-logs.old + %- ~(gas by *(map resource:store update-log:store)) + %+ murn ~(tap by update-logs.old) + |= [r=resource:store =update-log:store] + ?: (is-old-dm:upgrade:store r) + ~ + `[r (strip-sigs-log:upgrade:store update-log)] + :: + graphs.old + %- ~(gas by *(map resource:store marked-graph:store)) + %+ murn ~(tap by graphs.old) + |= [r=resource:store =graph:store mar=(unit mark)] + ?: (is-old-dm:upgrade:store r) + ~ + `[r (strip-sigs-graph:upgrade:store graph) mar] + :: cards ;: welp cards :: (nuke-groups:upgrade:store bowl) :: - ^- (list card) - %+ turn ~(tap by archive.old) - |= [r=resource:store m=marked-graph:store] - ^- card - =/ pax /(rap 3 'archive-' (scot %p entity.r) '-' name.r ~)/noun - =/ =cage drum-put+!>([pax (jam r m)]) - [%pass /archive %agent [our.bowl %hood] %poke cage] + (turn ~(tap by archive.old) backup) + (turn ~(tap by old-dms) backup) == == :: diff --git a/pkg/landscape/lib/graph-store.hoon b/pkg/landscape/lib/graph-store.hoon index 4fb283748..77d95a5e3 100644 --- a/pkg/landscape/lib/graph-store.hoon +++ b/pkg/landscape/lib/graph-store.hoon @@ -528,6 +528,65 @@ :: ++ upgrade |% + ++ is-old-dm |=(r=resource =('dm--' (end [3 4] name.r))) + ++ backup + |= =bowl:gall + |= [r=resource m=marked-graph] + ^- card:agent:gall + =/ pax /(rap 3 'archive-' (scot %p entity.r) '-' name.r ~)/noun + =/ =cage drum-put+!>([pax (jam r m)]) + [%pass /archive %agent [our.bowl %hood] %poke cage] + ++ strip-sigs-graph + |= g=graph + ^+ g + =* loop $ + %+ gas:orm *graph + %+ turn (tap:orm g) + |= [key=@ val=node] :: optional: also strip out deleted messages? + =? children.val ?=(%graph -.children.val) + [%graph loop(g p.children.val)] + :- key + ?. ?=(%& -.post.val) + val + val(signatures.p.post ~) + ++ strip-sigs-log + |= u=update-log + %+ gas:orm-log *update-log + %+ turn (tap:orm-log u) + |= [key=@ upd=logged-update] + :- key + :- p.upd + ?+ -.q.upd q.upd + %add-graph + q.upd(graph (strip-sigs-graph graph.q.upd)) + :: + %add-signatures + q.upd(signatures ~) + :: + %remove-signatures + q.upd(signatures ~) + :: + %add-nodes + %= q.upd + nodes + %- ~(run by nodes.q.upd) + |= =node + ^+ node + %= node + children + ?. ?=(%graph -.children.node) + children.node + [%graph (strip-sigs-graph p.children.node)] + :: + post + ?. ?=(%& -.post.node) + post.node + =. signatures.p.post.node ~ + post.node + == + == + == + :: ++ nuke-groups |= =bowl:gall |^ ^- (list card:agent:gall) From a90c85023c7c57bda71811ba00e65b43617939d8 Mon Sep 17 00:00:00 2001 From: Philip Monk Date: Mon, 28 Nov 2022 16:49:07 -0700 Subject: [PATCH 030/136] u3: add comment explaining willoc logic --- pkg/urbit/noun/allocate.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pkg/urbit/noun/allocate.c b/pkg/urbit/noun/allocate.c index f7ce67958..4667b7ba8 100644 --- a/pkg/urbit/noun/allocate.c +++ b/pkg/urbit/noun/allocate.c @@ -425,8 +425,18 @@ _ca_willoc(c3_w len_w, c3_w ald_w, c3_w alp_w) alp_w = (alp_w + c3_wiseof(u3a_box)) % ald_w; - // XX: this logic is totally bizarre, but preserve it. - // + /* XX: this logic is totally bizarre, but preserve it. + ** + ** This means we use the next size bigger instead of the "correct" + ** size. For example, a 20 word allocation will be freed into free + ** list 2 but will be allocated from free list 3. + ** + ** This is important to preserve because the sequential search may be + ** very slow. On a real-world task involving many compilations, + ** removing this line made this function appear in ~80% of samples. + ** + ** For reference, this was added in cgyarvin/urbit ffed9e748d8f6c. + */ if ( (sel_w != 0) && (sel_w != u3a_fbox_no - 1) ) { sel_w += 1; } From 06a2fe5b69293b97f98dd3a9162acd6cfba49ed7 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 29 Nov 2022 13:12:10 +1000 Subject: [PATCH 031/136] landscape: crash the event if migration fails --- pkg/landscape/app/group-store.hoon | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index f95dfccd9..845488e26 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -91,8 +91,8 @@ cards %- welp :_ cards - :~ [%pass / %agent [our dap]:bowl %poke noun+!>(%export)] - [%pass / %agent [our dap]:bowl %poke noun+!>(%migrate)] + :~ [%pass /pyre/export %agent [our dap]:bowl %poke noun+!>(%export)] + [%pass /pyre/migrate %agent [our dap]:bowl %poke noun+!>(%migrate)] [%pass / %agent [our %hood]:bowl %poke %kiln-install !>([%groups ~zod %groups])] == == @@ -186,6 +186,10 @@ ++ on-agent |= [=wire =sign:agent:gall] ^- (quip card _this) + ?: ?=([%pyre *] wire) + =^ cards state + (take-pyre:gc t.wire sign) + [cards this] ?: ?=([%gladio ~] wire) (on-agent:def wire sign) ?: ?=([%gladio @ ~] wire) @@ -244,6 +248,15 @@ =/ cards (welp cards-1 cards-2) [cards state(wait wait)] :: +++ take-pyre + |= [=wire =sign:agent:gall] + ^- (quip card _state) + :_ state + ?> ?=(%poke-ack -.sign) + ?~ p.sign + ~ + [%pass / %pyre leaf/"{} failed" u.p.sign]~ +:: ++ take-migrate |= =sign:agent:gall ^- (quip card _state) From 37339bed31b286d3cc98ed16b1d65f40ee7d7d44 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 29 Nov 2022 13:17:28 +1000 Subject: [PATCH 032/136] group-store: cleanup sign handling --- pkg/landscape/app/group-store.hoon | 44 ++++++++++++++---------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 845488e26..e57f27a6a 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -186,30 +186,26 @@ ++ on-agent |= [=wire =sign:agent:gall] ^- (quip card _this) - ?: ?=([%pyre *] wire) - =^ cards state - (take-pyre:gc t.wire sign) - [cards this] - ?: ?=([%gladio ~] wire) - (on-agent:def wire sign) - ?: ?=([%gladio @ ~] wire) - =^ cards state - (take-migrate:gc sign) - [cards this] - ?. ?=([%try-rejoin @ *] wire) - (on-agent:def wire sign) - ?> ?=(%poke-ack -.sign) - =/ rid=resource (de-path:resource t.t.wire) - ?~ p.sign - =/ =cage - [%pull-hook-action !>([%add entity.rid rid])] - :_ this - [%pass / %agent [our.bowl %group-pull-hook] %poke cage]~ - =/ nack-count=@ud (slav %ud i.t.wire) - =/ wakeup=@da - (add now.bowl (mul ~s1 (bex (min 19 nack-count)))) - :_ this - [%pass wire %arvo %b %wait wakeup]~ + =^ cards state + ?+ wire [- state]:(on-agent:def wire sign) + [%pyre *] (take-pyre:gc t.wire sign) + [%gladio @ ~] (take-migrate:gc sign) + :: + [%try-rejoin @ *] + ?> ?=(%poke-ack -.sign) + =/ rid=resource (de-path:resource t.t.wire) + ?~ p.sign + =/ =cage + [%pull-hook-action !>([%add entity.rid rid])] + :_ state + [%pass / %agent [our.bowl %group-pull-hook] %poke cage]~ + =/ nack-count=@ud (slav %ud i.t.wire) + =/ wakeup=@da + (add now.bowl (mul ~s1 (bex (min 19 nack-count)))) + :_ state + [%pass wire %arvo %b %wait wakeup]~ + == + [cards this] :: ++ on-arvo |= [=wire =sign-arvo] From 0a555981cc65f27c13a7dba445c87d935f7caf08 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 29 Nov 2022 13:25:15 +1000 Subject: [PATCH 033/136] landscape: optionally disable memory mitigations in OTA --- pkg/landscape/app/group-store.hoon | 6 +++--- pkg/landscape/lib/gladio.hoon | 29 +++++++++++++++++------------ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index e57f27a6a..bc6e5bae8 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -228,9 +228,9 @@ ++ poke-export ^- (quip card _state) :_ state - =; =(fyrd:khan cage) - [%pass /export %arvo %k %fard fyrd]~ - [q.byk.bol %keep noun+!>(~(export gladio bol))] + =; =cage + [%pass /export %agent [our.bol %hood] %poke cage]~ + drum-put+!>([/groups/jam ~(export gladio bol)]) :: ++ poke-migrate ^- (quip card _state) diff --git a/pkg/landscape/lib/gladio.hoon b/pkg/landscape/lib/gladio.hoon index 67bf9ab73..29b738667 100644 --- a/pkg/landscape/lib/gladio.hoon +++ b/pkg/landscape/lib/gladio.hoon @@ -6,6 +6,9 @@ /- *group |_ =bowl:gall +$ card card:agent:gall +:: if false, indicates that OTA should be done in one go, in order to +:: allow for testing on partial testnets +++ split-ota & ++ import-club |= [=^groups =associations:met =network:gra] %- ~(gas by *imports:club:i) @@ -74,10 +77,8 @@ ++ associations-raw .^(* (scry %metadata-store /export/noun)) ++ export - ~> %bout.[1 %export-jam] %- jam ^- * - ~> %bout.[1 %export-scry] :~ [%group-store groups-raw] [%metadata-store associations-raw] == @@ -143,16 +144,20 @@ heap-flags=(flag-importer %graph-validator-link) diary-flags=(flag-importer %graph-validator-publish) == - :_ ships - %+ welp (migrate-ship our.bowl) - :* (poke-our %groups group-import+!>(imports)) - (poke-our %chat import-flags+!>(chat-flags)) - (poke-our %heap import-flags+!>(heap-flags)) - (poke-our %diary import-flags+!>(diary-flags)) - (poke-our %chat club-imports+!>(clubs)) - ?~ dms ~ - (poke-our %chat dm-imports+!>(p.u.dms))^~ - == + =/ setup=(list card) + %+ welp (migrate-ship our.bowl) + :* (poke-our %groups group-import+!>(imports)) + (poke-our %chat import-flags+!>(chat-flags)) + (poke-our %heap import-flags+!>(heap-flags)) + (poke-our %diary import-flags+!>(diary-flags)) + (poke-our %chat club-imports+!>(clubs)) + ?~ dms ~ + (poke-our %chat dm-imports+!>(p.u.dms))^~ + == + ?. split-ota + :_ ~ + (welp setup (zing (turn ~(tap in (~(del in ships) our.bowl)) migrate-ship))) + [setup ships] :: ++ migrate-ship |= her=ship From ec23cc531801cbab3ee757ae22f12317bef9be39 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 30 Nov 2022 15:20:11 -0600 Subject: [PATCH 034/136] migration: correcting take-migrate ship --- pkg/landscape/app/group-store.hoon | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index bc6e5bae8..095fc0693 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -186,10 +186,11 @@ ++ on-agent |= [=wire =sign:agent:gall] ^- (quip card _this) + ~& src.bol =^ cards state ?+ wire [- state]:(on-agent:def wire sign) [%pyre *] (take-pyre:gc t.wire sign) - [%gladio @ ~] (take-migrate:gc sign) + [%gladio @ ~] (take-migrate:gc (scot %p i.t.wire) sign) :: [%try-rejoin @ *] ?> ?=(%poke-ack -.sign) @@ -236,6 +237,7 @@ ^- (quip card _state) =^ cards-1=(list card) wait ~(migrate-start gladio bol) + ~& wait =/ cards-2=(list card) %+ turn ~(tap in wait) |= =ship @@ -254,18 +256,18 @@ [%pass / %pyre leaf/"{} failed" u.p.sign]~ :: ++ take-migrate - |= =sign:agent:gall + |= [=ship =sign:agent:gall] ^- (quip card _state) - ~& migrating/src.bol + ~& [migrating ship src.bol] ?: ?=(%poke-ack -.sign) `state - :_ state(wait (~(del in wait) src.bol)) + :_ state(wait (~(del in wait) ship)) ^- (list card) - %+ welp (~(migrate-ship gladio bol) src.bol) + %+ welp (~(migrate-ship gladio bol) ship) ?: ?=(%kick -.sign) :: TODO: check queued watches don't get kicked *(list card) :_ *(list card) - [%pass /gladio/(scot %p src.bol) %agent [src.bol %groups] %leave ~] + [%pass /gladio/(scot %p ship) %agent [ship %groups] %leave ~] :: ++ peek-group |= rid=resource From 81419a8a033272750901b8b1f0bf77bb0d20ef75 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 30 Nov 2022 15:26:25 -0600 Subject: [PATCH 035/136] migration: fixing bad @tas --- pkg/landscape/app/group-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 095fc0693..f009d39dd 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -258,7 +258,7 @@ ++ take-migrate |= [=ship =sign:agent:gall] ^- (quip card _state) - ~& [migrating ship src.bol] + ~& [%migrating ship src.bol] ?: ?=(%poke-ack -.sign) `state :_ state(wait (~(del in wait) ship)) From 173b641df467846e24b482b0adac95622142a987 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 30 Nov 2022 15:31:56 -0600 Subject: [PATCH 036/136] migration: correctly parsing ship --- pkg/landscape/app/group-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index f009d39dd..9a8469453 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -190,7 +190,7 @@ =^ cards state ?+ wire [- state]:(on-agent:def wire sign) [%pyre *] (take-pyre:gc t.wire sign) - [%gladio @ ~] (take-migrate:gc (scot %p i.t.wire) sign) + [%gladio @ ~] (take-migrate:gc (slav %p i.t.wire) sign) :: [%try-rejoin @ *] ?> ?=(%poke-ack -.sign) From de1cdcefbb8181da7243f1208b5dd76a3f46cc01 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 30 Nov 2022 16:46:47 -0600 Subject: [PATCH 037/136] migration: undoing ship/src.bowl change, adding logging to imports --- pkg/landscape/app/group-store.hoon | 14 ++++++-------- pkg/landscape/lib/gladio.hoon | 2 ++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 9a8469453..bc6e5bae8 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -186,11 +186,10 @@ ++ on-agent |= [=wire =sign:agent:gall] ^- (quip card _this) - ~& src.bol =^ cards state ?+ wire [- state]:(on-agent:def wire sign) [%pyre *] (take-pyre:gc t.wire sign) - [%gladio @ ~] (take-migrate:gc (slav %p i.t.wire) sign) + [%gladio @ ~] (take-migrate:gc sign) :: [%try-rejoin @ *] ?> ?=(%poke-ack -.sign) @@ -237,7 +236,6 @@ ^- (quip card _state) =^ cards-1=(list card) wait ~(migrate-start gladio bol) - ~& wait =/ cards-2=(list card) %+ turn ~(tap in wait) |= =ship @@ -256,18 +254,18 @@ [%pass / %pyre leaf/"{} failed" u.p.sign]~ :: ++ take-migrate - |= [=ship =sign:agent:gall] + |= =sign:agent:gall ^- (quip card _state) - ~& [%migrating ship src.bol] + ~& migrating/src.bol ?: ?=(%poke-ack -.sign) `state - :_ state(wait (~(del in wait) ship)) + :_ state(wait (~(del in wait) src.bol)) ^- (list card) - %+ welp (~(migrate-ship gladio bol) ship) + %+ welp (~(migrate-ship gladio bol) src.bol) ?: ?=(%kick -.sign) :: TODO: check queued watches don't get kicked *(list card) :_ *(list card) - [%pass /gladio/(scot %p ship) %agent [ship %groups] %leave ~] + [%pass /gladio/(scot %p src.bol) %agent [src.bol %groups] %leave ~] :: ++ peek-group |= rid=resource diff --git a/pkg/landscape/lib/gladio.hoon b/pkg/landscape/lib/gladio.hoon index 29b738667..8828350b9 100644 --- a/pkg/landscape/lib/gladio.hoon +++ b/pkg/landscape/lib/gladio.hoon @@ -144,6 +144,7 @@ heap-flags=(flag-importer %graph-validator-link) diary-flags=(flag-importer %graph-validator-publish) == + ~& [%to-import chat-flags] =/ setup=(list card) %+ welp (migrate-ship our.bowl) :* (poke-our %groups group-import+!>(imports)) @@ -176,6 +177,7 @@ %. ~(key by links) =- ~(uni in -) (~(uni in ~(key by chats)) ~(key by diarys)) + ~& [%importing ~(key by chats)] %+ welp %+ turn ~(tap in graph-flags) |= =flag:i From 7ca9ebaa4dd3c471cf7714b392637b6842948410 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 30 Nov 2022 18:24:21 -0500 Subject: [PATCH 038/136] u3: updates memory protections and page metadata when moving guard page --- pkg/urbit/noun/events.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index ceec1bd41..bf2d4a197 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -1240,8 +1240,27 @@ void u3e_ward(u3_post low_p, u3_post hig_p) { #ifdef U3_GUARD_PAGE - if ( (low_p > gar_pag_p) || (hig_p < gar_pag_p) ) { + const u3p(c3_w) gar_p = gar_pag_p; + + if ( (low_p > gar_p) || (hig_p < gar_p) ) { _ce_center_guard_page(); + + if ( 0 != mprotect(u3a_into(gar_p), + pag_siz_i, + (PROT_READ | PROT_WRITE)) ) + { + fprintf(stderr, "loom: failed to unprotect old guard page: %s\r\n", + strerror(errno)); + c3_assert(0); + } + + { + c3_w pag_w = gar_p >> u3a_page; + c3_w blk_w = (pag_w >> 5); + c3_w bit_w = (pag_w & 31); + + u3P.dit_w[blk_w] |= (1 << bit_w); + } } #endif } From 0f6ae4b74aa4e0695d94cc6a6f733920c998e9d0 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 1 Dec 2022 11:22:41 +1000 Subject: [PATCH 039/136] group-store: fix flag declaration --- pkg/landscape/lib/gladio.hoon | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/landscape/lib/gladio.hoon b/pkg/landscape/lib/gladio.hoon index 8828350b9..1f13aaf04 100644 --- a/pkg/landscape/lib/gladio.hoon +++ b/pkg/landscape/lib/gladio.hoon @@ -28,10 +28,14 @@ `[flag members.u.group u.assoc graph] :: ++ import-flags - |= [=^groups =associations:met =network:gra] + |= [our=ship =^groups =associations:met =network:gra] |= =mark ^- (set flag:i) - ~(key by ((import-for-mark ~ groups associations network) mark)) + %- ~(gas in *(set flag:i)) + %+ skim + ~(tap in ~(key by ((import-for-mark ~ groups associations network) mark))) + |= =flag:i + !=(our p.flag) :: ++ import-for-mark |= [her=(unit ship) =^groups =associations:met =network:gra] @@ -139,7 +143,7 @@ ~ `[flag u.assoc chans roles group] =/ dms (~(get by graphs:network) [our.bowl %dm-inbox]) - =/ flag-importer (import-flags groups associations network) + =/ flag-importer (import-flags our.bowl groups associations network) =+ :* chat-flags=(flag-importer %graph-validator-chat) heap-flags=(flag-importer %graph-validator-link) diary-flags=(flag-importer %graph-validator-publish) From feb305b59e5b3984e7713e35ff754637020ab9c8 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 30 Nov 2022 20:12:17 -0600 Subject: [PATCH 040/136] Revert "migration: undoing ship/src.bowl change, adding logging to imports" This reverts commit de1cdcefbb8181da7243f1208b5dd76a3f46cc01. --- pkg/landscape/app/group-store.hoon | 14 ++++++++------ pkg/landscape/lib/gladio.hoon | 2 -- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index bc6e5bae8..9a8469453 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -186,10 +186,11 @@ ++ on-agent |= [=wire =sign:agent:gall] ^- (quip card _this) + ~& src.bol =^ cards state ?+ wire [- state]:(on-agent:def wire sign) [%pyre *] (take-pyre:gc t.wire sign) - [%gladio @ ~] (take-migrate:gc sign) + [%gladio @ ~] (take-migrate:gc (slav %p i.t.wire) sign) :: [%try-rejoin @ *] ?> ?=(%poke-ack -.sign) @@ -236,6 +237,7 @@ ^- (quip card _state) =^ cards-1=(list card) wait ~(migrate-start gladio bol) + ~& wait =/ cards-2=(list card) %+ turn ~(tap in wait) |= =ship @@ -254,18 +256,18 @@ [%pass / %pyre leaf/"{} failed" u.p.sign]~ :: ++ take-migrate - |= =sign:agent:gall + |= [=ship =sign:agent:gall] ^- (quip card _state) - ~& migrating/src.bol + ~& [%migrating ship src.bol] ?: ?=(%poke-ack -.sign) `state - :_ state(wait (~(del in wait) src.bol)) + :_ state(wait (~(del in wait) ship)) ^- (list card) - %+ welp (~(migrate-ship gladio bol) src.bol) + %+ welp (~(migrate-ship gladio bol) ship) ?: ?=(%kick -.sign) :: TODO: check queued watches don't get kicked *(list card) :_ *(list card) - [%pass /gladio/(scot %p src.bol) %agent [src.bol %groups] %leave ~] + [%pass /gladio/(scot %p ship) %agent [ship %groups] %leave ~] :: ++ peek-group |= rid=resource diff --git a/pkg/landscape/lib/gladio.hoon b/pkg/landscape/lib/gladio.hoon index 8828350b9..29b738667 100644 --- a/pkg/landscape/lib/gladio.hoon +++ b/pkg/landscape/lib/gladio.hoon @@ -144,7 +144,6 @@ heap-flags=(flag-importer %graph-validator-link) diary-flags=(flag-importer %graph-validator-publish) == - ~& [%to-import chat-flags] =/ setup=(list card) %+ welp (migrate-ship our.bowl) :* (poke-our %groups group-import+!>(imports)) @@ -177,7 +176,6 @@ %. ~(key by links) =- ~(uni in -) (~(uni in ~(key by chats)) ~(key by diarys)) - ~& [%importing ~(key by chats)] %+ welp %+ turn ~(tap in graph-flags) |= =flag:i From cae675f4d47f37d1e099312a82ec33a4a89ee4ed Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 30 Nov 2022 20:12:23 -0600 Subject: [PATCH 041/136] Revert "migration: correctly parsing ship" This reverts commit 173b641df467846e24b482b0adac95622142a987. --- pkg/landscape/app/group-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 9a8469453..f009d39dd 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -190,7 +190,7 @@ =^ cards state ?+ wire [- state]:(on-agent:def wire sign) [%pyre *] (take-pyre:gc t.wire sign) - [%gladio @ ~] (take-migrate:gc (slav %p i.t.wire) sign) + [%gladio @ ~] (take-migrate:gc (scot %p i.t.wire) sign) :: [%try-rejoin @ *] ?> ?=(%poke-ack -.sign) From 92c6ffa283c86fd32bc425c3e968dd4b29b5e9e1 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 30 Nov 2022 20:13:23 -0600 Subject: [PATCH 042/136] Revert "migration: fixing bad @tas" This reverts commit 81419a8a033272750901b8b1f0bf77bb0d20ef75. --- pkg/landscape/app/group-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index f009d39dd..095fc0693 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -258,7 +258,7 @@ ++ take-migrate |= [=ship =sign:agent:gall] ^- (quip card _state) - ~& [%migrating ship src.bol] + ~& [migrating ship src.bol] ?: ?=(%poke-ack -.sign) `state :_ state(wait (~(del in wait) ship)) From c3d4c538aa56c85e2af117c85d76668cff245e66 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 30 Nov 2022 20:13:42 -0600 Subject: [PATCH 043/136] Revert "migration: correcting take-migrate ship" This reverts commit ec23cc531801cbab3ee757ae22f12317bef9be39. --- pkg/landscape/app/group-store.hoon | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 095fc0693..bc6e5bae8 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -186,11 +186,10 @@ ++ on-agent |= [=wire =sign:agent:gall] ^- (quip card _this) - ~& src.bol =^ cards state ?+ wire [- state]:(on-agent:def wire sign) [%pyre *] (take-pyre:gc t.wire sign) - [%gladio @ ~] (take-migrate:gc (scot %p i.t.wire) sign) + [%gladio @ ~] (take-migrate:gc sign) :: [%try-rejoin @ *] ?> ?=(%poke-ack -.sign) @@ -237,7 +236,6 @@ ^- (quip card _state) =^ cards-1=(list card) wait ~(migrate-start gladio bol) - ~& wait =/ cards-2=(list card) %+ turn ~(tap in wait) |= =ship @@ -256,18 +254,18 @@ [%pass / %pyre leaf/"{} failed" u.p.sign]~ :: ++ take-migrate - |= [=ship =sign:agent:gall] + |= =sign:agent:gall ^- (quip card _state) - ~& [migrating ship src.bol] + ~& migrating/src.bol ?: ?=(%poke-ack -.sign) `state - :_ state(wait (~(del in wait) ship)) + :_ state(wait (~(del in wait) src.bol)) ^- (list card) - %+ welp (~(migrate-ship gladio bol) ship) + %+ welp (~(migrate-ship gladio bol) src.bol) ?: ?=(%kick -.sign) :: TODO: check queued watches don't get kicked *(list card) :_ *(list card) - [%pass /gladio/(scot %p ship) %agent [ship %groups] %leave ~] + [%pass /gladio/(scot %p src.bol) %agent [src.bol %groups] %leave ~] :: ++ peek-group |= rid=resource From 0970c046570e33d2381e1eabb596579a0a4910f5 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Fri, 2 Dec 2022 15:55:05 +0400 Subject: [PATCH 044/136] meta: opportunistically gut deprecated workflows --- .github/workflows/chromatic.yml | 27 --------------------------- .github/workflows/glob.yml | 20 -------------------- .github/workflows/merge-master.yml | 27 --------------------------- .github/workflows/merge-release.yml | 17 ----------------- 4 files changed, 91 deletions(-) delete mode 100644 .github/workflows/chromatic.yml delete mode 100644 .github/workflows/glob.yml delete mode 100644 .github/workflows/merge-master.yml delete mode 100644 .github/workflows/merge-release.yml diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml deleted file mode 100644 index 1d8711f7f..000000000 --- a/.github/workflows/chromatic.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Chromatic Deployment - -on: - pull_request: - paths: - - 'pkg/interface/**' - push: - paths: - - 'pkg/interface/**' - branches: - - 'release/next-userspace' - -jobs: - chromatic-deployment: - runs-on: ubuntu-latest - name: "Deploy Chromatic" - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - run: npm i && npm run bootstrap - - name: Publish to Chromatic - uses: chromaui/action@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} - workingDir: pkg/interface diff --git a/.github/workflows/glob.yml b/.github/workflows/glob.yml deleted file mode 100644 index be5d77115..000000000 --- a/.github/workflows/glob.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: glob -on: - push: - branches: - - 'release/next-userspace' -jobs: - glob: - runs-on: ubuntu-latest - name: "Create and deploy a glob to ~hanruc-nalfus-nidsut-tomdun" - steps: - - uses: actions/checkout@v2 - with: - lfs: true - - uses: ./.github/actions/glob - with: - ship: 'hanruc-nalfus-nidsut-tomdun' - credentials: ${{ secrets.JANEWAY_SERVICE_KEY }} - ssh-sec-key: ${{ secrets.JANEWAY_SSH_SEC_KEY }} - ssh-pub-key: ${{ secrets.JANEWAY_SSH_PUB_KEY }} - diff --git a/.github/workflows/merge-master.yml b/.github/workflows/merge-master.yml deleted file mode 100644 index 48d0abf9f..000000000 --- a/.github/workflows/merge-master.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: merge -on: - push: - branches: - - 'master' -jobs: - merge-to-next-js: - runs-on: ubuntu-latest - name: "Merge master to release/next-userspace" - steps: - - uses: actions/checkout@v2 - - uses: devmasx/merge-branch@v1.3.1 - with: - type: now - target_branch: release/next-userspace - github_token: ${{ secrets.JANEWAY_BOT_TOKEN }} - - merge-to-group-timer: - runs-on: ubuntu-latest - name: "Merge master to ops/group-timer" - steps: - - uses: actions/checkout@v2 - - uses: devmasx/merge-branch@v1.3.1 - with: - type: now - target_branch: ops/group-timer - github_token: ${{ secrets.JANEWAY_BOT_TOKEN }} diff --git a/.github/workflows/merge-release.yml b/.github/workflows/merge-release.yml deleted file mode 100644 index eb7df325a..000000000 --- a/.github/workflows/merge-release.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: ops-merge -on: - push: - branches: - - 'release/*' -jobs: - merge-release-to-ops: - runs-on: ubuntu-latest - name: "Merge to ops-tlon" - steps: - - uses: actions/checkout@v2 - - uses: devmasx/merge-branch@v1.3.1 - with: - type: now - target_branch: ops-tlon - github_token: ${{ secrets.JANEWAY_BOT_TOKEN }} - From ad563795af34d16f45efc6b36fc6bae0951c203a Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Fri, 2 Dec 2022 15:58:04 +0400 Subject: [PATCH 045/136] meta: cull deprecated glob action --- .github/actions/glob/Dockerfile | 4 ---- .github/actions/glob/action.yml | 25 ----------------------- .github/actions/glob/entrypoint.sh | 32 ------------------------------ 3 files changed, 61 deletions(-) delete mode 100644 .github/actions/glob/Dockerfile delete mode 100644 .github/actions/glob/action.yml delete mode 100755 .github/actions/glob/entrypoint.sh diff --git a/.github/actions/glob/Dockerfile b/.github/actions/glob/Dockerfile deleted file mode 100644 index e372532ac..000000000 --- a/.github/actions/glob/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM tloncorp/janeway:v0.15.4 -COPY entrypoint.sh /entrypoint.sh -EXPOSE 22/tcp -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/actions/glob/action.yml b/.github/actions/glob/action.yml deleted file mode 100644 index 4617e3d49..000000000 --- a/.github/actions/glob/action.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: 'glob' -description: 'Create a glob and deploy it to a moon' -inputs: - ship: - description: "Ship to deploy to" - required: true - credentials: - description: "base64-encoded GCP Service Account credentials" - required: true - ssh-sec-key: - description: "A base64-encoded SSH secret key for the container to use" - required: true - ssh-pub-key: - description: "The corresponding base64-encoded SSH public key" - required: true - -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.ship }} - - ${{ inputs.credentials }} - - ${{ inputs.ssh-sec-key }} - - ${{ inputs.ssh-pub-key }} - diff --git a/.github/actions/glob/entrypoint.sh b/.github/actions/glob/entrypoint.sh deleted file mode 100755 index 7428213a5..000000000 --- a/.github/actions/glob/entrypoint.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -cd "$GITHUB_WORKSPACE" || exit - -echo "$2" | base64 -d > service-account -echo "$3" | base64 -d > id_ssh -echo "$4" | base64 -d > id_ssh.pub - -chmod 600 service-account -chmod 600 id_ssh -chmod 600 id_ssh.pub - -janeway release glob-all --dev --no-pill \ - --credentials service-account \ - --ssh-key id_ssh \ - --ci \ - | bash - -SHORTHASH=$(git rev-parse --short HEAD) - -janeway release prepare-ota arvo-glob-"$SHORTHASH" "$1" \ - --credentials service-account \ - --ssh-key id_ssh \ - --ci \ - | bash - -janeway release perform-ota "$1" \ - --credentials service-account \ - --ssh-key id_ssh \ - --ci \ - | bash - From 775e3715ba20c508e2432170021cd69f5ff86169 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 2 Dec 2022 08:38:21 -0500 Subject: [PATCH 046/136] vere: updates whereami dep to support OpenBSD fixes urbit/urbit#6100 --- pkg/urbit/daemon/whereami.c | 207 +++++++++++++++++++++++++++++------- pkg/urbit/daemon/whereami.h | 6 +- 2 files changed, 173 insertions(+), 40 deletions(-) diff --git a/pkg/urbit/daemon/whereami.c b/pkg/urbit/daemon/whereami.c index 290005766..5e31a2490 100644 --- a/pkg/urbit/daemon/whereami.c +++ b/pkg/urbit/daemon/whereami.c @@ -1,4 +1,6 @@ -// (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz) +// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses +// without any warranty. +// by Gregory Pakosz (@gpakosz) // https://github.com/gpakosz/whereami // in case you want to #include "whereami.c" in a larger compilation unit @@ -10,6 +12,15 @@ extern "C" { #endif +#if defined(__linux__) || defined(__CYGWIN__) +#undef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#elif defined(__APPLE__) +#undef _DARWIN_C_SOURCE +#define _DARWIN_C_SOURCE +#define _DARWIN_BETTER_REALPATH +#endif + #if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC) #include #endif @@ -46,7 +57,9 @@ extern "C" { #if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif #if defined(_MSC_VER) #pragma warning(push, 3) #endif @@ -55,6 +68,7 @@ extern "C" { #if defined(_MSC_VER) #pragma warning(pop) #endif +#include static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length) { @@ -62,8 +76,9 @@ static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, i wchar_t buffer2[MAX_PATH]; wchar_t* path = NULL; int length = -1; + bool ok; - for (;;) + for (ok = false; !ok; ok = true) { DWORD size; int length_, length__; @@ -119,14 +134,12 @@ static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, i } length = length__; - - break; } if (path != buffer1) WAI_FREE(path); - return length; + return ok ? length : -1; } WAI_NOINLINE WAI_FUNCSPEC @@ -156,7 +169,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) return length; } -#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) +#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE) #include #include @@ -170,6 +183,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) #define __STDC_FORMAT_MACROS #endif #include +#include #if !defined(WAI_PROC_SELF_EXE) #if defined(__sun) @@ -185,8 +199,9 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) char buffer[PATH_MAX]; char* resolved = NULL; int length = -1; + bool ok; - for (;;) + for (ok = false; !ok; ok = true) { resolved = realpath(WAI_PROC_SELF_EXE, buffer); if (!resolved) @@ -211,11 +226,9 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) } } } - - break; } - return length; + return ok ? length : -1; } #if !defined(WAI_PROC_SELF_MAPS_RETRY) @@ -235,6 +248,7 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) #include #include #endif +#include WAI_NOINLINE WAI_FUNCSPEC int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) @@ -281,15 +295,24 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) &&buffer[length - 4] == '.') { int fd = open(path, O_RDONLY); - char* begin; - char* p; + if (fd == -1) + { + length = -1; // retry + break; + } - begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0); - p = begin + offset; + char* begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0); + if (begin == MAP_FAILED) + { + close(fd); + length = -1; // retry + break; + } + char* p = begin + offset - 30; // minimum size of local file header while (p >= begin) // scan backwards { - if (*((uint32_t*)p) == 0x04034b50UL) // local file header found + if (*((uint32_t*)p) == 0x04034b50UL) // local file header signature found { uint16_t length_ = *((uint16_t*)(p + 26)); @@ -303,7 +326,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) break; } - p -= 4; + --p; } munmap(begin, offset); @@ -341,20 +364,17 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) break; } - if (maps) - fclose(maps); - return length; } #elif defined(__APPLE__) -#define _DARWIN_BETTER_REALPATH #include #include #include #include #include +#include WAI_FUNCSPEC int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) @@ -364,8 +384,9 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) char* path = buffer1; char* resolved = NULL; int length = -1; + bool ok; - for (;;) + for (ok = false; !ok; ok = true) { uint32_t size = (uint32_t)sizeof(buffer1); if (_NSGetExecutablePath(path, &size) == -1) @@ -398,14 +419,12 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) } } } - - break; } if (path != buffer1) WAI_FREE(path); - return length; + return ok ? length : -1; } WAI_NOINLINE WAI_FUNCSPEC @@ -459,6 +478,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) #include #include #include +#include #if !defined(WAI_PROC_SELF_EXE) #define WAI_PROC_SELF_EXE "/proc/self/exefile" @@ -472,8 +492,9 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) char* resolved = NULL; FILE* self_exe = NULL; int length = -1; + bool ok; - for (;;) + for (ok = false; !ok; ok = true) { self_exe = fopen(WAI_PROC_SELF_EXE, "r"); if (!self_exe) @@ -505,13 +526,11 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) } } } - - break; } fclose(self_exe); - return length; + return ok ? length : -1; } WAI_FUNCSPEC @@ -559,7 +578,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) } #elif defined(__DragonFly__) || defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || defined(__NetBSD__) + defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) #include #include @@ -567,6 +586,116 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) #include #include #include +#include + +#if defined(__OpenBSD__) + +#include + +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[4096]; + char buffer2[PATH_MAX]; + char buffer3[PATH_MAX]; + char** argv = (char**)buffer1; + char* resolved = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV }; + size_t size; + + if (sysctl(mib, 4, NULL, &size, NULL, 0) != 0) + break; + + if (size > sizeof(buffer1)) + { + argv = (char**)WAI_MALLOC(size); + if (!argv) + break; + } + + if (sysctl(mib, 4, argv, &size, NULL, 0) != 0) + break; + + if (strchr(argv[0], '/')) + { + resolved = realpath(argv[0], buffer2); + if (!resolved) + break; + } + else + { + const char* PATH = getenv("PATH"); + if (!PATH) + break; + + size_t argv0_length = strlen(argv[0]); + + const char* begin = PATH; + while (1) + { + const char* separator = strchr(begin, ':'); + const char* end = separator ? separator : begin + strlen(begin); + + if (end - begin > 0) + { + if (*(end -1) == '/') + --end; + + if (((end - begin) + 1 + argv0_length + 1) <= sizeof(buffer2)) + { + memcpy(buffer2, begin, end - begin); + buffer2[end - begin] = '/'; + memcpy(buffer2 + (end - begin) + 1, argv[0], argv0_length + 1); + + resolved = realpath(buffer2, buffer3); + if (resolved) + break; + } + } + + if (!separator) + break; + + begin = ++separator; + } + + if (!resolved) + break; + } + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + if (argv != (char**)buffer1) + WAI_FREE(argv); + + return ok ? length : -1; +} + +#else WAI_FUNCSPEC int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) @@ -576,13 +705,18 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) char* path = buffer1; char* resolved = NULL; int length = -1; + bool ok; - for (;;) + for (ok = false; !ok; ok = true) { +#if defined(__NetBSD__) + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; +#else int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; +#endif size_t size = sizeof(buffer1); - if (sysctl(mib, (u_int)(sizeof(mib) / sizeof(mib[0])), path, &size, NULL, 0) != 0) + if (sysctl(mib, 4, path, &size, NULL, 0) != 0) break; resolved = realpath(path, buffer2); @@ -608,16 +742,13 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) } } } - - break; } - if (path != buffer1) - WAI_FREE(path); - - return length; + return ok ? length : -1; } +#endif + WAI_NOINLINE WAI_FUNCSPEC int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) { @@ -670,4 +801,4 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/pkg/urbit/daemon/whereami.h b/pkg/urbit/daemon/whereami.h index 6c81af818..d5edffb8c 100644 --- a/pkg/urbit/daemon/whereami.h +++ b/pkg/urbit/daemon/whereami.h @@ -1,4 +1,6 @@ -// (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz) +// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses +// without any warranty. +// by Gregory Pakosz (@gpakosz) // https://github.com/gpakosz/whereami #ifndef WHEREAMI_H @@ -62,4 +64,4 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length); } #endif -#endif // #ifndef WHEREAMI_H +#endif // #ifndef WHEREAMI_H \ No newline at end of file From fee50375c9252f4b45aee9a148a442e13badae5f Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 2 Dec 2022 09:57:29 -0500 Subject: [PATCH 047/136] u3: document snapshot system invariants --- pkg/urbit/noun/events.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 48b32d151..114794e47 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -50,6 +50,28 @@ //! - memory protections (and file-backed mappings) are re-established. //! - patch files are deleted. //! +//! ### invariants +//! +//! definitions: +//! - a clean page is PROT_READ and 0 in the bitmap +//! - a dirty page is (PROT_READ|PROT_WRITE) and 1 in the bitmap +//! - the guard page is PROT_NONE and 1 in the bitmap (XX assumed) +//! +//! assumptions: +//! - all memory access patterns are outside-in, a page at a time +//! - ad-hoc exceptions are supported by calling u3e_ward() +//! +//! - there is a single guard page, between the segments +//! - dirty pages only become clean by being: +//! - loaded from a snapshot during initialization +//! - present in a snapshot after save +//! - clean pages only become dirty by being: +//! - modified (and caught by the fault handler) +//! - orphaned due to segment truncation (explicitly dirtied) +//! - at points of quiescence (initialization, after save) +//! - all pages of the north and south segments are clean +//! - all other pages are dirty +//! //! ### limitations //! //! - loom page size is fixed (16 KB), and must be a multiple of the From d1d1860ac72c137613fe0dfc88e58a8bbf9673f6 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 2 Dec 2022 09:57:44 -0500 Subject: [PATCH 048/136] u3: cleanup comments about snapshot system limitations --- pkg/urbit/noun/events.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 114794e47..730801ee1 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -75,14 +75,13 @@ //! ### limitations //! //! - loom page size is fixed (16 KB), and must be a multiple of the -//! system page size. (can the size vary at runtime give south.bin's -//! reversed order? alternately, if system page size > ours, the fault -//! handler could dirty N pages at a time.) -//! - update atomicity is suspect: patch application must either -//! completely succeed or leave on-disk segments intact. unapplied -//! patches can be discarded (triggering event replay), but once -//! patch application begins it must succeed. -//! may require integration into the overall signal-handling regime. +//! system page size. +//! - update atomicity is crucial: +//! - patch application must either completely succeed or +//! leave on-disk segments (memory image) intact. +//! - unapplied patches can be discarded (triggering event replay), +//! but once patch application begins it must succeed. +//! - may require integration into the overall signal-handling regime. //! - many errors are handled with assertions. //! //! ### enhancements From c111129a5e84c521cbedb105453d6f837b44333a Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 2 Dec 2022 09:19:14 -0500 Subject: [PATCH 049/136] u3: adds new, batch page tracking implementations + tests --- pkg/urbit/noun/events.c | 85 ++++++++++++ pkg/urbit/tests/events_tests.c | 238 +++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100644 pkg/urbit/tests/events_tests.c diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 730801ee1..31fc7c6e4 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -775,6 +775,91 @@ _ce_loom_pure_south(c3_w pgs_w) u3P.dit_w[bas_w - 1] &= 0xffffffff >> bit_w; } +/* _ce_loom_track_north(): [pgs_w] clean, followed by [dif_w] dirty. +*/ +void +_ce_loom_track_north(c3_w pgs_w, c3_w dif_w) +{ + c3_w blk_w = pgs_w >> 5; + c3_w bit_w = pgs_w & 31; + c3_w off_w; + + memset((void*)u3P.dit_w, 0, blk_w << 2); + + if ( bit_w ) { + c3_w tib_w = 32 - bit_w; + c3_w dat_w = u3P.dit_w[blk_w]; + + dat_w &= 0xffffffff << bit_w; + + if ( dif_w <= tib_w ) { + dat_w |= ((1 << dif_w) - 1) << bit_w; + dif_w = 0; + } + else { + dat_w |= 0xffffffff << bit_w; + dif_w -= tib_w; + } + + u3P.dit_w[blk_w] = dat_w; + blk_w += 1; + } + + off_w = blk_w; + blk_w = dif_w >> 5; + bit_w = dif_w & 31; + + memset((void*)(u3P.dit_w + off_w), 0xff, blk_w << 2); + + if ( bit_w ) { + u3P.dit_w[off_w + blk_w] |= (1 << bit_w) - 1; + } +} + +/* _ce_loom_track_south(): [pgs_w] clean, preceded by [dif_w] dirty. +*/ +void +_ce_loom_track_south(c3_w pgs_w, c3_w dif_w) +{ + c3_w blk_w = pgs_w >> 5; + c3_w bit_w = pgs_w & 31; + c3_w bas_w = ((u3P.pag_w - pgs_w) + 31) >> 5; + c3_w off_w; + + memset((void*)(u3P.dit_w + bas_w), 0, blk_w << 2); + + // the following index subtractions (bas_w, off) are safe, + // so long as the south segment never includes all pages + // + if ( bit_w ) { + c3_w tib_w = 32 - bit_w; + c3_w dat_w = u3P.dit_w[--bas_w]; + + dat_w &= 0xffffffff >> bit_w; + + if ( dif_w <= tib_w ) { + dat_w |= ((1 << dif_w) - 1) << (tib_w - dif_w); + dif_w = 0; + } + else { + dat_w |= 0xffffffff >> bit_w; + dif_w -= tib_w; + } + + u3P.dit_w[bas_w] = dat_w; + } + + blk_w = dif_w >> 5; + bit_w = dif_w & 31; + off_w = bas_w - blk_w; + + memset((void*)(u3P.dit_w + off_w), 0xff, blk_w << 2); + + if ( bit_w ) { + u3P.dit_w[off_w - 1] |= ((1 << bit_w) - 1) << (32 - bit_w); + } +} + /* _ce_loom_protect_north(): protect/track pages from the bottom of memory. */ static void diff --git a/pkg/urbit/tests/events_tests.c b/pkg/urbit/tests/events_tests.c new file mode 100644 index 000000000..f8615efcb --- /dev/null +++ b/pkg/urbit/tests/events_tests.c @@ -0,0 +1,238 @@ +#include "all.h" + +/* _setup(): prepare for tests. +*/ +static void +_setup(void) +{ + // NB: no loom + // + u3P.pag_w = u3a_pages; +} + +static c3_w +_check_north_clean(void) +{ + c3_w i_w, pag_w, blk_w, bit_w; + + for ( i_w = 0; i_w < u3P.pag_w; i_w++ ) { + pag_w = i_w; + blk_w = pag_w >> 5; + bit_w = pag_w & 31; + + if ( u3P.dit_w[blk_w] & (1 << bit_w) ) { + break; + } + } + + return i_w; +} + +static c3_w +_check_north_dirty(c3_w pgs_w, c3_w max_w) +{ + c3_w i_w, pag_w, blk_w, bit_w; + + for ( i_w = 0; i_w < max_w; i_w++ ) { + pag_w = i_w + pgs_w; + blk_w = pag_w >> 5; + bit_w = pag_w & 31; + + if ( !(u3P.dit_w[blk_w] & (1 << bit_w)) ) { + break; + } + } + + return i_w; +} + +static c3_w +_check_south_clean(void) +{ + c3_w i_w, pag_w, blk_w, bit_w; + + for ( i_w = 0; i_w < u3P.pag_w; i_w++ ) { + pag_w = u3P.pag_w - (i_w + 1); + blk_w = pag_w >> 5; + bit_w = pag_w & 31; + + if ( u3P.dit_w[blk_w] & (1 << bit_w) ) { + break; + } + } + + return i_w; +} + +static c3_w +_check_south_dirty(c3_w pgs_w, c3_w max_w) +{ + c3_w i_w, pag_w, blk_w, bit_w; + + for ( i_w = 0; i_w < max_w; i_w++ ) { + pag_w = u3P.pag_w - (i_w + pgs_w + 1); + blk_w = pag_w >> 5; + bit_w = pag_w & 31; + + if ( !(u3P.dit_w[blk_w] & (1 << bit_w)) ) { + break; + } + } + + return i_w; +} + +void +_ce_loom_track_north(c3_w pgs_w, c3_w dif_w); +void +_ce_loom_track_south(c3_w pgs_w, c3_w dif_w); + +static c3_i +_test_tracking(void) +{ + c3_w ret_w; + + u3e_foul(); + + if ( 0 != (ret_w = _check_north_clean()) ) { + fprintf(stderr, "test events track north init %u\r\n", ret_w); + return 0; + } + + if ( 0 != (ret_w = _check_south_clean()) ) { + fprintf(stderr, "test events track south init %u\r\n", ret_w); + return 0; + } + + _ce_loom_track_north(100, 0); + _ce_loom_track_south(1, 0); + + if ( 100 != (ret_w = _check_north_clean()) ) { + fprintf(stderr, "test events track north clean a %u\r\n", ret_w); + return 0; + } + + if ( 1 != (ret_w = _check_south_clean()) ) { + fprintf(stderr, "test events track south clean a %u\r\n", ret_w); + return 0; + } + + _ce_loom_track_north(75, 25); + _ce_loom_track_south(2, 0); + + if ( 75 != (ret_w = _check_north_clean()) ) { + fprintf(stderr, "test events track north clean b %u\r\n", ret_w); + return 0; + } + + if ( 25 != (ret_w = _check_north_dirty(75, 25)) ) { + fprintf(stderr, "test events track north dirty b %u\r\n", ret_w); + return 0; + } + + if ( 2 != (ret_w = _check_south_clean()) ) { + fprintf(stderr, "test events track south clean b %u\r\n", ret_w); + return 0; + } + + _ce_loom_track_north(55, 20); + _ce_loom_track_south(1, 1); + + if ( 55 != (ret_w = _check_north_clean()) ) { + fprintf(stderr, "test events track north clean c %u\r\n", ret_w); + return 0; + } + + if ( 20 != (ret_w = _check_north_dirty(55, 20)) ) { + fprintf(stderr, "test events track north dirty c %u\r\n", ret_w); + return 0; + } + + if ( 1 != (ret_w = _check_south_clean()) ) { + fprintf(stderr, "test events track south clean c %u\r\n", ret_w); + return 0; + } + + if ( 1 != (ret_w = _check_south_dirty(1, 1)) ) { + fprintf(stderr, "test events track north dirty c %u\r\n", ret_w); + return 0; + } + + _ce_loom_track_north(255, 0); + _ce_loom_track_south(48, 0); + + if ( 255 != (ret_w = _check_north_clean()) ) { + fprintf(stderr, "test events track north clean d %u\r\n", ret_w); + return 0; + } + + if ( 48 != (ret_w = _check_south_clean()) ) { + fprintf(stderr, "test events track south clean d %u\r\n", ret_w); + return 0; + } + + _ce_loom_track_north(213, 42); + _ce_loom_track_south(15, 33); + + if ( 213 != (ret_w = _check_north_clean()) ) { + fprintf(stderr, "test events track north clean e %u\r\n", ret_w); + return 0; + } + + if ( 42 != (ret_w = _check_north_dirty(213, 42)) ) { + fprintf(stderr, "test events track north dirty e %u\r\n", ret_w); + return 0; + } + + if ( 15 != (ret_w = _check_south_clean()) ) { + fprintf(stderr, "test events track south clean e %u\r\n", ret_w); + return 0; + } + + if ( 33 != (ret_w = _check_south_dirty(15, 33)) ) { + fprintf(stderr, "test events track north dirty e %u\r\n", ret_w); + return 0; + } + + _ce_loom_track_north(200, 13); + _ce_loom_track_south(10, 5); + + if ( 200 != (ret_w = _check_north_clean()) ) { + fprintf(stderr, "test events track north clean f %u\r\n", ret_w); + return 0; + } + + if ( 13 != (ret_w = _check_north_dirty(200, 13)) ) { + fprintf(stderr, "test events track north dirty f %u\r\n", ret_w); + return 0; + } + + if ( 10 != (ret_w = _check_south_clean()) ) { + fprintf(stderr, "test events track south clean f %u\r\n", ret_w); + return 0; + } + + if ( 5 != (ret_w = _check_south_dirty(10, 5)) ) { + fprintf(stderr, "test events track north dirty f %u\r\n", ret_w); + return 0; + } + + return 1; +} + +/* main(): run all test cases. +*/ +int +main(int argc, char* argv[]) +{ + _setup(); + + if ( !_test_tracking() ) { + fprintf(stderr, "test_events: tracking: failed\r\n"); + exit(1); + } + + fprintf(stderr, "test_events: ok\n"); + + return 0; +} From a7022e75a0d5325ae1386bb08f67db2a83434d43 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 2 Dec 2022 09:58:32 -0500 Subject: [PATCH 050/136] u3: enforce snapshot invariants on segment truncation --- pkg/urbit/noun/events.c | 94 +++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 31fc7c6e4..b4c082010 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -747,34 +747,6 @@ _ce_patch_apply(u3_ce_patch* pat_u) } } -/* _ce_loom_pure_north(): track clean pages at the bottom of memory. -*/ -static inline void -_ce_loom_pure_north(c3_w pgs_w) -{ - c3_w blk_w = pgs_w >> 5; - c3_w bit_w = pgs_w & 31; - - memset((void*)u3P.dit_w, 0, blk_w << 2); - u3P.dit_w[blk_w] &= 0xffffffff << bit_w; -} - -/* _ce_loom_pure_south(): track clean pages at the top of memory. -*/ -static inline void -_ce_loom_pure_south(c3_w pgs_w) -{ - c3_w blk_w = pgs_w >> 5; - c3_w bit_w = pgs_w & 31; - c3_w bas_w = ((u3P.pag_w - pgs_w) + 31) >> 5; - - memset((void*)(u3P.dit_w + bas_w), 0, blk_w << 2); - - // this is safe so long as the south segment never includes all pages - // - u3P.dit_w[bas_w - 1] &= 0xffffffff >> bit_w; -} - /* _ce_loom_track_north(): [pgs_w] clean, followed by [dif_w] dirty. */ void @@ -863,39 +835,71 @@ _ce_loom_track_south(c3_w pgs_w, c3_w dif_w) /* _ce_loom_protect_north(): protect/track pages from the bottom of memory. */ static void -_ce_loom_protect_north(c3_w pgs_w) +_ce_loom_protect_north(c3_w pgs_w, c3_w old_w) { if ( pgs_w ) { if ( 0 != mprotect((void*)u3_Loom, (size_t)pgs_w << (u3a_page + 2), PROT_READ) ) { - fprintf(stderr, "loom: protect north (%u pages): %s\r\n", + fprintf(stderr, "loom: pure north (%u pages): %s\r\n", pgs_w, strerror(errno)); c3_assert(0); } } - _ce_loom_pure_north(pgs_w); + if ( old_w > pgs_w ) { + c3_w dif_w = old_w - pgs_w; + + if ( 0 != mprotect((void*)(u3_Loom + (pgs_w << u3a_page)), + (size_t)dif_w << (u3a_page + 2), + (PROT_READ | PROT_WRITE)) ) + { + fprintf(stderr, "loom: foul north (%u pages, %u old): %s\r\n", + pgs_w, old_w, strerror(errno)); + c3_assert(0); + } + + _ce_loom_track_north(pgs_w, dif_w); + } + else { + _ce_loom_track_north(pgs_w, 0); + } } /* _ce_loom_protect_south(): protect/track pages from the top of memory. */ static void -_ce_loom_protect_south(c3_w pgs_w) +_ce_loom_protect_south(c3_w pgs_w, c3_w old_w) { if ( pgs_w ) { if ( 0 != mprotect((void*)(u3_Loom + ((u3P.pag_w - pgs_w) << u3a_page)), (size_t)pgs_w << (u3a_page + 2), PROT_READ) ) { - fprintf(stderr, "loom: protect south (%u pages): %s\r\n", + fprintf(stderr, "loom: pure south (%u pages): %s\r\n", pgs_w, strerror(errno)); c3_assert(0); } } - _ce_loom_pure_south(pgs_w); + if ( old_w > pgs_w ) { + c3_w dif_w = old_w - pgs_w; + + if ( 0 != mprotect((void*)(u3_Loom + ((u3P.pag_w - old_w) << u3a_page)), + (size_t)dif_w << (u3a_page + 2), + (PROT_READ | PROT_WRITE)) ) + { + fprintf(stderr, "loom: foul south (%u pages, %u old): %s\r\n", + pgs_w, old_w, strerror(errno)); + c3_assert(0); + } + + _ce_loom_track_south(pgs_w, dif_w); + } + else { + _ce_loom_track_south(pgs_w, 0); + } } /* _ce_loom_mapf_north(): map [pgs_w] of [fid_i] into the bottom of memory @@ -926,8 +930,10 @@ _ce_loom_mapf_north(c3_i fid_i, c3_w pgs_w, c3_w old_w) } if ( old_w > pgs_w ) { + c3_w dif_w = old_w - pgs_w; + if ( MAP_FAILED == mmap((void*)(u3_Loom + (pgs_w << u3a_page)), - (size_t)(old_w - pgs_w) << (u3a_page + 2), + (size_t)dif_w << (u3a_page + 2), (PROT_READ | PROT_WRITE), (MAP_ANON | MAP_FIXED | MAP_PRIVATE), -1, 0) ) @@ -936,9 +942,12 @@ _ce_loom_mapf_north(c3_i fid_i, c3_w pgs_w, c3_w old_w) pgs_w, old_w, strerror(errno)); c3_assert(0); } - } - _ce_loom_pure_north(pgs_w); + _ce_loom_track_north(pgs_w, dif_w); + } + else { + _ce_loom_track_north(pgs_w, 0); + } } /* _ce_loom_blit_north(): apply pages, in order, from the bottom of memory. @@ -962,7 +971,7 @@ _ce_loom_blit_north(c3_i fid_i, c3_w pgs_w) } } - _ce_loom_protect_north(pgs_w); + _ce_loom_protect_north(pgs_w, 0); } /* _ce_loom_blit_south(): apply pages, reversed, from the top of memory. @@ -991,7 +1000,7 @@ _ce_loom_blit_south(c3_i fid_i, c3_w pgs_w) } } - _ce_loom_protect_south(pgs_w); + _ce_loom_protect_south(pgs_w, 0); } #ifdef U3_SNAPSHOT_VALIDATION @@ -1157,7 +1166,7 @@ void u3e_save(void) { u3_ce_patch* pat_u; - c3_w nod_w; + c3_w nod_w, sod_w; if ( u3C.wag_w & u3o_dryrun ) { return; @@ -1168,6 +1177,7 @@ u3e_save(void) } nod_w = u3P.nor_u.pgs_w; + sod_w = u3P.sou_u.pgs_w; _ce_patch_sync(pat_u); @@ -1193,13 +1203,13 @@ u3e_save(void) #endif if ( u3C.wag_w & u3o_no_demand ) { - _ce_loom_protect_north(u3P.nor_u.pgs_w); + _ce_loom_protect_north(u3P.nor_u.pgs_w, nod_w); } else { _ce_loom_mapf_north(u3P.nor_u.fid_i, u3P.nor_u.pgs_w, nod_w); } - _ce_loom_protect_south(u3P.sou_u.pgs_w); + _ce_loom_protect_south(u3P.sou_u.pgs_w, sod_w); _ce_image_sync(&u3P.nor_u); _ce_image_sync(&u3P.sou_u); From 62a575f63ebe76c3671777d49145e5644e756f61 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 2 Dec 2022 11:55:30 -0500 Subject: [PATCH 051/136] u3: switch page size constants to macros to avoid VLAs --- pkg/urbit/noun/events.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index b4c082010..df8d501b1 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -99,10 +99,10 @@ static u3p(c3_w) gar_pag_p; //! Urbit page size in 4-byte words. -static const size_t pag_wiz_i = (size_t)1 << u3a_page; +#define pag_wiz_i ((size_t)1 << u3a_page) //! Urbit page size in bytes. -static const size_t pag_siz_i = (size_t)1 << (u3a_page + 2); +#define pag_siz_i ((size_t)1 << (u3a_page + 2)) #ifdef U3_SNAPSHOT_VALIDATION /* Image check. From 66c54ad39289f14bdd25bfe0a36f9a346de48eb2 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Mon, 14 Nov 2022 12:24:20 -0500 Subject: [PATCH 052/136] u3: protect guard page if necessary after remapping loom --- pkg/urbit/noun/events.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index b1fba886b..cff61829f 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -943,6 +943,19 @@ _ce_loom_mapf_north(c3_i fid_i, c3_w pgs_w, c3_w old_w) c3_assert(0); } + // protect guard page if clobbered + // + // NB: < pgs_w is precluded by assertion in _ce_patch_compose() + // + if ( (gar_pag_p >> u3a_page) < old_w ) { + fprintf(stderr, "loom: guard on remap\r\n"); + if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { + fprintf(stderr, "loom: failed to protect guard page: %s\r\n", + strerror(errno)); + c3_assert(0); + } + } + _ce_loom_track_north(pgs_w, dif_w); } else { From 5693c965dcdaef08bc5bd2415be7baeea62ba565 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 2 Dec 2022 16:39:18 -0500 Subject: [PATCH 053/136] u3: protect guard page if necessary after reprotecting loom --- pkg/urbit/noun/events.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index cff61829f..8020f65d2 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -860,6 +860,19 @@ _ce_loom_protect_north(c3_w pgs_w, c3_w old_w) c3_assert(0); } + // protect guard page if clobbered + // + // NB: < pgs_w is precluded by assertion in _ce_patch_compose() + // + if ( (gar_pag_p >> u3a_page) < old_w ) { + fprintf(stderr, "loom: guard on reprotect\r\n"); + if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { + fprintf(stderr, "loom: failed to protect guard page: %s\r\n", + strerror(errno)); + c3_assert(0); + } + } + _ce_loom_track_north(pgs_w, dif_w); } else { @@ -895,6 +908,19 @@ _ce_loom_protect_south(c3_w pgs_w, c3_w old_w) c3_assert(0); } + // protect guard page if clobbered + // + // NB: > pgs_w is precluded by assertion in _ce_patch_compose() + // + if ( (gar_pag_p >> u3a_page) >= (u3a_pages - (old_w + 1)) ) { + fprintf(stderr, "loom: guard on reprotect\r\n"); + if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { + fprintf(stderr, "loom: failed to protect guard page: %s\r\n", + strerror(errno)); + c3_assert(0); + } + } + _ce_loom_track_south(pgs_w, dif_w); } else { From 15c4ae7fa71713d6d12c0eaf455470be350f040c Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 2 Dec 2022 16:54:27 -0500 Subject: [PATCH 054/136] u3: updates guard-page assertion to account for variable loom sizes --- pkg/urbit/noun/events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 8020f65d2..a9004dd12 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -590,7 +590,7 @@ _ce_patch_compose(void) sou_w = (swu_w + (pag_wiz_i - 1)) >> u3a_page; c3_assert( ((gar_pag_p >> u3a_page) >= nor_w) - && ((gar_pag_p >> u3a_page) <= (u3a_pages - (sou_w + 1))) ); + && ((gar_pag_p >> u3a_page) <= (u3P.pag_w - (sou_w + 1))) ); } #ifdef U3_SNAPSHOT_VALIDATION From 95a2e06f4408adfcd816d3ebbdb70841e7f78020 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 2 Dec 2022 16:55:08 -0500 Subject: [PATCH 055/136] test: re-disables ames decryption test to avoid bail:evil in ci --- pkg/arvo/tests/sys/vane/ames.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/tests/sys/vane/ames.hoon b/pkg/arvo/tests/sys/vane/ames.hoon index bca47314d..acab4f96e 100644 --- a/pkg/arvo/tests/sys/vane/ames.hoon +++ b/pkg/arvo/tests/sys/vane/ames.hoon @@ -251,7 +251,7 @@ !> shut-packet !> decoded :: -++ test-shut-packet-associated-data ^- tang +++ disabled-test-shut-packet-associated-data ^- tang :: =/ =shut-packet:ames :+ bone=17 message-num=18 From 7870024df9f06e09a8713e8607e9cfedb7c97abb Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 2 Dec 2022 16:56:00 -0500 Subject: [PATCH 056/136] test: re-disables failing grq test --- pkg/arvo/tests/sys/grq.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/tests/sys/grq.hoon b/pkg/arvo/tests/sys/grq.hoon index 7bf1e0a6b..f7a85c105 100644 --- a/pkg/arvo/tests/sys/grq.hoon +++ b/pkg/arvo/tests/sys/grq.hoon @@ -2,7 +2,7 @@ :: /+ *test, v=test-ames-gall |% -++ test-watch +++ disabled-test-watch %- run-chain |. :- %| =+ nec-bud:v From 1ac2264a49e9abd470618e3033a9ea2c7ad2294c Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 10 Nov 2022 12:38:35 -0500 Subject: [PATCH 057/136] test: renames nock-tests to meme-tests --- pkg/urbit/tests/{nock_tests.c => meme_tests.c} | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) rename pkg/urbit/tests/{nock_tests.c => meme_tests.c} (82%) diff --git a/pkg/urbit/tests/nock_tests.c b/pkg/urbit/tests/meme_tests.c similarity index 82% rename from pkg/urbit/tests/nock_tests.c rename to pkg/urbit/tests/meme_tests.c index a96ed09bd..e52e4c028 100644 --- a/pkg/urbit/tests/nock_tests.c +++ b/pkg/urbit/tests/meme_tests.c @@ -5,10 +5,7 @@ static void _setup(void) { - // XX at 1<<24, this succeeds on mac, but bail:exit's on linux. - // investigate possible u3n_prog corruption - // - u3m_init(1 << 25); + u3m_init(1 << 24); u3m_pave(c3y); u3e_init(); } @@ -53,7 +50,7 @@ _test_nock_meme(void) } static c3_i -_test_nock(void) +_test_meme(void) { c3_i ret_i = 1; @@ -72,8 +69,8 @@ main(int argc, char* argv[]) { _setup(); - if ( !_test_nock() ) { - fprintf(stderr, "test nock: failed\r\n"); + if ( !_test_meme() ) { + fprintf(stderr, "test meme: failed\r\n"); exit(1); } @@ -81,6 +78,6 @@ main(int argc, char* argv[]) // u3m_grab(u3_none); - fprintf(stderr, "test nock: ok\r\n"); + fprintf(stderr, "test meme: ok\r\n"); return 0; } From a4c597d281670655329dbdfee9c933df22eafd22 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 5 Dec 2022 11:07:40 +1000 Subject: [PATCH 058/136] graph-pull-hook: do not archive on nack --- pkg/landscape/app/graph-pull-hook.hoon | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pkg/landscape/app/graph-pull-hook.hoon b/pkg/landscape/app/graph-pull-hook.hoon index 1a134e614..364e0d00d 100644 --- a/pkg/landscape/app/graph-pull-hook.hoon +++ b/pkg/landscape/app/graph-pull-hook.hoon @@ -54,12 +54,7 @@ ++ on-pull-nack |= [=resource =tang] ^- (quip card _this) - %- (slog leaf+"nacked {}" tang) - :_ this - ?. (~(has in get-keys:gra) resource) ~ - =- [%pass /pull-nack %agent [our.bowl %graph-store] %poke %graph-update-3 -]~ - !> ^- update:store - [now.bowl [%archive-graph resource]] + `this :: ++ on-pull-kick |= =resource From e4827915d8cb096fb96a0c057c6c7cbd6043a896 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 5 Dec 2022 13:08:51 +1000 Subject: [PATCH 059/136] group-pull-hook: disabling remove on nack --- pkg/landscape/app/group-pull-hook.hoon | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/landscape/app/group-pull-hook.hoon b/pkg/landscape/app/group-pull-hook.hoon index f97582e70..aad34e33b 100644 --- a/pkg/landscape/app/group-pull-hook.hoon +++ b/pkg/landscape/app/group-pull-hook.hoon @@ -45,10 +45,7 @@ ++ on-pull-nack |= [=resource =tang] ^- (quip card _this) - %- (slog tang) - :_ this - =- [%pass / %agent [our.bowl %group-store] %poke -]~ - group-update-0+!>([%remove-group resource ~]) + `this :: ++ on-pull-kick |= =resource From 6841f726a7ad25d9968935ae07ba73b0e58f4a34 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 5 Dec 2022 13:09:30 +1000 Subject: [PATCH 060/136] metadata-pull-hook: disabling remove on nack --- pkg/landscape/app/metadata-pull-hook.hoon | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/pkg/landscape/app/metadata-pull-hook.hoon b/pkg/landscape/app/metadata-pull-hook.hoon index f2d08fd30..2877a098d 100644 --- a/pkg/landscape/app/metadata-pull-hook.hoon +++ b/pkg/landscape/app/metadata-pull-hook.hoon @@ -321,15 +321,7 @@ ++ on-pull-nack |= [=resource =tang] ^- (quip card _this) - =/ =associations:metadata - (metadata-for-group:met resource) - :_ this - %+ turn ~(tap by associations) - |= [=md-resource:metadata =association:metadata] - %+ poke-our:pass:io:hc %metadata-store - :- %metadata-update-2 - !> ^- update:metadata - [%remove resource md-resource] + `this :: ++ on-pull-kick |= =resource From e600ded69c9d9961948887435951af0ceedb351f Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 7 Dec 2022 08:30:04 +1000 Subject: [PATCH 061/136] migration: install %talk --- pkg/landscape/app/group-store.hoon | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index bc6e5bae8..0233ba2c6 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -34,6 +34,7 @@ /+ gladio |% +$ card card:agent:gall ++$ ota-host ~zod :: +$ versioned-state $% state-zero @@ -93,7 +94,8 @@ :_ cards :~ [%pass /pyre/export %agent [our dap]:bowl %poke noun+!>(%export)] [%pass /pyre/migrate %agent [our dap]:bowl %poke noun+!>(%migrate)] - [%pass / %agent [our %hood]:bowl %poke %kiln-install !>([%groups ~zod %groups])] + [%pass / %agent [our %hood]:bowl %poke %kiln-install !>([%groups ota-host %groups])] + [%pass / %agent [our %hood]:bowl %poke %kiln-install !>([%talk ota-host %talk])] == == :: From fbb2bd865ed3e4b26fa4d25680da6749fcfccf59 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 7 Dec 2022 08:36:35 +1000 Subject: [PATCH 062/136] migration: uninstall through %docket --- pkg/landscape/lib/graph-store.hoon | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/landscape/lib/graph-store.hoon b/pkg/landscape/lib/graph-store.hoon index 77d95a5e3..d81afb866 100644 --- a/pkg/landscape/lib/graph-store.hoon +++ b/pkg/landscape/lib/graph-store.hoon @@ -594,7 +594,8 @@ ~ =+ .^(=desk (gall-scry %d %groups)) :~ [%pass /nuke %agent [our.bowl %hood] %poke kiln-nuke+!>([desk &])] - [%pass /nuke %agent [our.bowl %hood] %poke kiln-uninstall+!>(desk)] + [%pass /nuke %agent [our.bowl %docket] %poke docket-uninstall+!>(desk)] + [%pass /nuke %agent [our.bowl %docket] %poke docket-uninstall+!>(%talk)] == :: ++ gall-scry From 45b9686b06d7b63c1f40331e189beb3cbf7ea507 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 7 Dec 2022 08:41:01 +1000 Subject: [PATCH 063/136] migration: remove docket for landscape --- pkg/landscape/desk.docket-0 | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 pkg/landscape/desk.docket-0 diff --git a/pkg/landscape/desk.docket-0 b/pkg/landscape/desk.docket-0 deleted file mode 100644 index e6b65c6ac..000000000 --- a/pkg/landscape/desk.docket-0 +++ /dev/null @@ -1,10 +0,0 @@ -:~ title+'Groups' - info+'A suite of applications to communicate on Urbit' - color+0xee.5432 - glob-http+['https://bootstrap.urbit.org/glob-0v7.2rpmd.966js.dt2sj.ggv4a.n15nq.glob' 0v7.2rpmd.966js.dt2sj.ggv4a.n15nq] - - base+'landscape' - version+[1 1 0] - website+'https://tlon.io' - license+'MIT' -== From 42996f14cc3519a4298a5af868d6a57a80561492 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 7 Dec 2022 08:54:53 +1000 Subject: [PATCH 064/136] docket: remove charge if docket file no longer exists --- pkg/garden/app/docket.hoon | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/garden/app/docket.hoon b/pkg/garden/app/docket.hoon index 671834ccc..54e595a69 100644 --- a/pkg/garden/app/docket.hoon +++ b/pkg/garden/app/docket.hoon @@ -394,7 +394,10 @@ :: !=(%kids desk) :: == :: [dap.bowl %no-docket-file-for desk] - `state + ?. (~(has by charges) desk) + `state + :- ~[del-fact:cha] + state(charges (~(del by charges) desk)) :: always update the docket in state to match clay's :: =/ =docket docket:cha From e4560339ecd4226a0d71966a719582b213b39a91 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Thu, 8 Dec 2022 07:53:00 -0600 Subject: [PATCH 065/136] migration: fixing state versions --- pkg/landscape/app/graph-store.hoon | 11 +++++++---- pkg/landscape/lib/graph-store.hoon | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/pkg/landscape/app/graph-store.hoon b/pkg/landscape/app/graph-store.hoon index aad097514..a828f0109 100644 --- a/pkg/landscape/app/graph-store.hoon +++ b/pkg/landscape/app/graph-store.hoon @@ -11,16 +11,17 @@ [%3 network:one:store] [%4 network:store] [%5 network:store] - state-6 + [%6 network:store] + state-7 == ::- -+$ state-6 [%6 network:store] ++$ state-7 [%7 network:store] ++ orm orm:store ++ orm-log orm-log:store ++ mar %graph-update-3 -- :: -=| state-6 +=| state-7 =* state - :: %- agent:dbug @@ -96,7 +97,9 @@ (scag 2 (tap:orm-log update-log)) == :: - %6 [cards this(state old)] + %6 $(-.old %7) + :: + %7 [cards this(state old)] == :: ++ on-watch diff --git a/pkg/landscape/lib/graph-store.hoon b/pkg/landscape/lib/graph-store.hoon index df11d0349..b4acc7a94 100644 --- a/pkg/landscape/lib/graph-store.hoon +++ b/pkg/landscape/lib/graph-store.hoon @@ -758,9 +758,9 @@ -- ++ import |= [arc=* our=ship] - ^- (quip card:agent:gall [%6 network]) + ^- (quip card:agent:gall [%7 network]) |^ - =/ sty [%6 (remake-network ;;(tree-network +.arc))] + =/ sty [%7 (remake-network ;;(tree-network +.arc))] :_ sty %+ turn ~(tap by graphs.sty) |= [rid=resource =marked-graph] From d4cce14ff462577558e7eff2a9ecc08488762ddb Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Thu, 8 Dec 2022 07:56:21 -0600 Subject: [PATCH 066/136] bill: removing notify --- pkg/landscape/desk.bill | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/landscape/desk.bill b/pkg/landscape/desk.bill index a050ee619..573beb789 100644 --- a/pkg/landscape/desk.bill +++ b/pkg/landscape/desk.bill @@ -29,7 +29,6 @@ %metadata-hook %metadata-pull-hook %metadata-push-hook - %notify %observe-hook %sane %weather From 8ac999593a755a0cd6e98124aaa267005cc1a384 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Thu, 8 Dec 2022 10:55:03 -0600 Subject: [PATCH 067/136] migration: update source --- pkg/landscape/app/group-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 0233ba2c6..3f5e660cd 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -34,7 +34,7 @@ /+ gladio |% +$ card card:agent:gall -+$ ota-host ~zod +++ ota-host ~marnec-dozzod-marzod :: +$ versioned-state $% state-zero From 9fc6cf7a401e460ae15a14f921a2842c6799d423 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Thu, 8 Dec 2022 11:01:48 -0600 Subject: [PATCH 068/136] kelvin: update --- pkg/landscape/sys.kelvin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/sys.kelvin b/pkg/landscape/sys.kelvin index e77a3de08..b7bcb9ecd 100644 --- a/pkg/landscape/sys.kelvin +++ b/pkg/landscape/sys.kelvin @@ -1 +1 @@ -[%zuse 418] +[%zuse 417] From 1a1dec62689572d43cac01b8ae7222b57a853b22 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 9 Dec 2022 09:21:09 +1000 Subject: [PATCH 069/136] group-store: handle alpha correctly --- pkg/landscape/app/group-store.hoon | 61 +++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 3f5e660cd..4b7301676 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -210,12 +210,16 @@ [cards this] :: ++ on-arvo - |= [=wire =sign-arvo] + |= [=(pole knot) =sign-arvo] ^- (quip card _this) - ?. ?=([%try-rejoin @ *] wire) - (on-arvo:def wire sign-arvo) - =/ =resource (de-path:resource t.t.wire) - =/ nack-count=@ud (slav %ud i.t.wire) + ?: ?=([%gladio %backoff ship=@ ~] pole) + =^ cards state + (take-backoff:gc (slav %p ship.pole) sign-arvo) + [cards this] + ?. ?=([%try-rejoin count=@ res=*] pole) + (on-arvo:def pole sign-arvo) + =/ =resource (de-path:resource res.pole) + =/ nack-count=@ud (slav %ud count.pole) ?> ?=([%behn %wake *] sign-arvo) ~? ?=(^ error.sign-arvo) "behn errored in backoff timers, continuing anyway" @@ -239,13 +243,20 @@ =^ cards-1=(list card) wait ~(migrate-start gladio bol) =/ cards-2=(list card) - %+ turn ~(tap in wait) - |= =ship - ^- card - [%pass /gladio/(scot %p ship) %agent [ship %groups] %watch /init] + (turn ~(tap in wait) watch-init-migrate) =/ cards (welp cards-1 cards-2) [cards state(wait wait)] :: +++ watch-init-migrate + |= =ship + ^- card + [%pass /gladio/(scot %p ship) %agent [ship %groups] %watch /init] +:: +++ backoff-migrate + |= =ship + ^- card + [%pass /gladio/backoff/(scot %p ship) %arvo %b %wait (add ~h1 now.bol)] +:: ++ take-pyre |= [=wire =sign:agent:gall] ^- (quip card _state) @@ -255,19 +266,33 @@ ~ [%pass / %pyre leaf/"{} failed" u.p.sign]~ :: +++ take-backoff + |= [=ship sign=sign-arvo] + ^- (quip card _state) + ?> ?=([%behn %wake *] sign) + ?: ?=(^ error.sign) + `state + :_ state + ~[(watch-init-migrate ship)] +:: ++ take-migrate |= =sign:agent:gall ^- (quip card _state) - ~& migrating/src.bol - ?: ?=(%poke-ack -.sign) + ?. (~(has in wait) src.bol) + :: already succeeded `state - :_ state(wait (~(del in wait) src.bol)) - ^- (list card) - %+ welp (~(migrate-ship gladio bol) src.bol) - ?: ?=(%kick -.sign) :: TODO: check queued watches don't get kicked - *(list card) - :_ *(list card) - [%pass /gladio/(scot %p src.bol) %agent [src.bol %groups] %leave ~] + ?- -.sign + ?(%poke-ack %fact) `state + %kick :_(state (watch-init-migrate src.bol)^~) + %watch-ack + ?~ p.sign + :: they have public release + ~& migrating/src.bol + :_ state(wait (~(del in wait) src.bol)) + (~(migrate-ship gladio bol) src.bol) + :_ state + ~[(backoff-migrate src.bol)] + == :: ++ peek-group |= rid=resource From eedc50f9e40f21027e894503c47af4cb99d0711d Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 9 Dec 2022 12:22:22 +1000 Subject: [PATCH 070/136] group-store: add /wait endpoints --- pkg/landscape/app/group-store.hoon | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 4b7301676..fe05ba813 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -154,6 +154,8 @@ |= =path ^- (quip card _this) ?> (team:title our.bowl src.bowl) + ?: ?=([%wait ~] path) + `this ?> ?=([%groups ~] path) :_ this [%give %fact ~ %group-update-0 !>([%initial groups])]~ @@ -164,6 +166,8 @@ |= =path ^- (unit (unit cage)) ?+ path (on-peek:def path) + [%x %wait ~] + ``ships+!>(~(tap in wait)) [%y %groups ~] ``noun+!>(`(set resource)`~(key by groups)) :: @@ -288,7 +292,9 @@ ?~ p.sign :: they have public release ~& migrating/src.bol - :_ state(wait (~(del in wait) src.bol)) + =. wait (~(del in wait) src.bol) + :_ state + :- [%give %fact ~[/wait] ships+!>(wait)] (~(migrate-ship gladio bol) src.bol) :_ state ~[(backoff-migrate src.bol)] From cf2c73a0e1c688119e98e4927d363735584dfa36 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 9 Dec 2022 12:39:47 +1000 Subject: [PATCH 071/136] group-store: make %migrate idempotent --- pkg/landscape/app/group-store.hoon | 2 +- pkg/landscape/lib/gladio.hoon | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index fe05ba813..7e26a80ff 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -245,7 +245,7 @@ ++ poke-migrate ^- (quip card _state) =^ cards-1=(list card) wait - ~(migrate-start gladio bol) + (~(migrate-start gladio bol) wait) =/ cards-2=(list card) (turn ~(tap in wait) watch-init-migrate) =/ cards (welp cards-1 cards-2) diff --git a/pkg/landscape/lib/gladio.hoon b/pkg/landscape/lib/gladio.hoon index c11ec7f63..a46f3d968 100644 --- a/pkg/landscape/lib/gladio.hoon +++ b/pkg/landscape/lib/gladio.hoon @@ -95,6 +95,7 @@ |= [=dude:gall =cage] [%pass /gladio %agent [our.bowl dude] %poke cage] ++ migrate-start + |= wait=(set ship) ^- (quip card (set ship)) =+ network =+ associations @@ -161,7 +162,7 @@ ?. split-ota :_ ~ (welp setup (zing (turn ~(tap in (~(del in ships) our.bowl)) migrate-ship))) - [setup ships] + [setup (~(uni in ships) wait)] :: ++ migrate-ship |= her=ship From 38919352c8113e41643198f21eaf6b4f8d6f4616 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Fri, 9 Dec 2022 11:07:51 -0600 Subject: [PATCH 072/136] migration: adding crash fix --- pkg/landscape/app/group-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 7e26a80ff..bdb2ca877 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -294,7 +294,7 @@ ~& migrating/src.bol =. wait (~(del in wait) src.bol) :_ state - :- [%give %fact ~[/wait] ships+!>(wait)] + :- [%give %fact ~[/wait] ships+!>(~(tap in wait))] (~(migrate-ship gladio bol) src.bol) :_ state ~[(backoff-migrate src.bol)] From ff0231e7619b2b87d86fc39de37f0ce3dd1aad48 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Fri, 9 Dec 2022 12:22:04 -0600 Subject: [PATCH 073/136] migration: set official ota source --- pkg/landscape/app/group-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 7e26a80ff..1dcfb63eb 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -34,7 +34,7 @@ /+ gladio |% +$ card card:agent:gall -++ ota-host ~marnec-dozzod-marzod +++ ota-host ~sogryp-dister-dozzod-dozzod :: +$ versioned-state $% state-zero From 46202122604077048f0525fe4662078ebb3e3674 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Tue, 6 Dec 2022 15:53:29 -0500 Subject: [PATCH 074/136] u3: removes u3e_foul() --- pkg/urbit/include/noun/events.h | 5 ----- pkg/urbit/noun/events.c | 14 +++++--------- pkg/urbit/noun/urth.c | 8 -------- pkg/urbit/tests/events_tests.c | 14 +++++++++++++- 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/pkg/urbit/include/noun/events.h b/pkg/urbit/include/noun/events.h index c1b95cde8..9b8376c89 100644 --- a/pkg/urbit/include/noun/events.h +++ b/pkg/urbit/include/noun/events.h @@ -80,11 +80,6 @@ c3_o u3e_yolo(void); - /* u3e_foul(): dirty all the pages of the loom. - */ - void - u3e_foul(void); - /* u3e_init(): initialize guard page tracking. */ void diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index a9004dd12..1ad2a27cd 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -1328,7 +1328,7 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) // mark all pages dirty (pages in the snapshot will be marked clean) // - u3e_foul(); + _ce_loom_track_north(0, u3P.pag_w); /* Write image files to memory; reinstate protection. */ @@ -1387,15 +1387,11 @@ u3e_yolo(void) c3_assert(0); } - return c3y; -} + // mark all pages dirty + // + _ce_loom_track_north(0, u3P.pag_w); -/* u3e_foul(): dirty all the pages of the loom. -*/ -void -u3e_foul(void) -{ - memset((void*)u3P.dit_w, 0xff, sizeof(u3P.dit_w)); + return c3y; } /* u3e_init(): initialize guard page tracking. diff --git a/pkg/urbit/noun/urth.c b/pkg/urbit/noun/urth.c index 0b8f02294..009442e9e 100644 --- a/pkg/urbit/noun/urth.c +++ b/pkg/urbit/noun/urth.c @@ -416,10 +416,6 @@ _cu_realloc(FILE* fil_u, ur_root_t** tor_u, ur_nvec_t* doc_u) // u3A->eve_d = eve_d; - // mark all pages dirty - // - u3e_foul(); - *tor_u = rot_u; *doc_u = cod_u; @@ -847,10 +843,6 @@ u3u_uncram(c3_c* dir_c, c3_d eve_d) // u3A->eve_d = eve_d; - // mark all pages dirty - // - u3e_foul(); - // leave rocks on disk // // if ( 0 != c3_unlink(nam_c) ) { diff --git a/pkg/urbit/tests/events_tests.c b/pkg/urbit/tests/events_tests.c index f8615efcb..f42ce5625 100644 --- a/pkg/urbit/tests/events_tests.c +++ b/pkg/urbit/tests/events_tests.c @@ -92,7 +92,12 @@ _test_tracking(void) { c3_w ret_w; - u3e_foul(); + _ce_loom_track_north(0, u3P.pag_w); + + if ( u3P.pag_w != (ret_w = _check_north_dirty(0, u3P.pag_w)) ) { + fprintf(stderr, "test events track north dirty all %u\r\n", ret_w); + return 0; + } if ( 0 != (ret_w = _check_north_clean()) ) { fprintf(stderr, "test events track north init %u\r\n", ret_w); @@ -217,6 +222,13 @@ _test_tracking(void) return 0; } + _ce_loom_track_north(0, u3P.pag_w); + + if ( u3P.pag_w != (ret_w = _check_north_dirty(0, u3P.pag_w)) ) { + fprintf(stderr, "test events track north dirty all %u\r\n", ret_w); + return 0; + } + return 1; } From bceb9cde4a76cb23c384f6d824271ad04cbb0337 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 7 Dec 2022 17:22:33 -0500 Subject: [PATCH 075/136] u3: stop using u3P.dir_c outside of events.c --- pkg/urbit/noun/manage.c | 2 ++ pkg/urbit/worker/serf.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 12d4d0fe9..65f0cd4b9 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -1867,6 +1867,8 @@ u3m_boot(c3_c* dir_c, size_t len_i) { c3_o nuu_o; + u3C.dir_c = dir_c; + /* Activate the loom. */ u3m_init(len_i); diff --git a/pkg/urbit/worker/serf.c b/pkg/urbit/worker/serf.c index 85420a122..f48b6f291 100644 --- a/pkg/urbit/worker/serf.c +++ b/pkg/urbit/worker/serf.c @@ -200,7 +200,7 @@ _serf_grab(u3_noun sac) c3_c* wen_c = u3r_string(wen); c3_c nam_c[2048]; - snprintf(nam_c, 2048, "%s/.urb/put/mass", u3P.dir_c); + snprintf(nam_c, 2048, "%s/.urb/put/mass", u3C.dir_c); struct stat st; if ( -1 == stat(nam_c, &st) ) { @@ -839,7 +839,7 @@ _serf_writ_live_exit(u3_serf* sef_u, c3_w cod_w) c3_c* wen_c = u3r_string(wen); c3_c nam_c[2048]; - snprintf(nam_c, 2048, "%s/.urb/put/profile", u3P.dir_c); + snprintf(nam_c, 2048, "%s/.urb/put/profile", u3C.dir_c); struct stat st; if ( -1 == stat(nam_c, &st) ) { From 39d0250afab9c5f84e0fb94593b6b3cce3947338 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 7 Dec 2022 17:31:24 -0500 Subject: [PATCH 076/136] u3: move directory creation out of events.c --- pkg/urbit/include/noun/events.h | 2 +- pkg/urbit/noun/events.c | 47 ++++++++++----------------------- pkg/urbit/noun/manage.c | 30 ++++++++++++++++++++- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/pkg/urbit/include/noun/events.h b/pkg/urbit/include/noun/events.h index 9b8376c89..e0bd58123 100644 --- a/pkg/urbit/include/noun/events.h +++ b/pkg/urbit/include/noun/events.h @@ -39,7 +39,7 @@ /* u3e_pool: entire memory system. */ typedef struct _u3e_pool { - c3_c* dir_c; // path to + c3_c* dir_c; // checkpoint dir c3_w dit_w[u3a_pages >> 5]; // touched since last save c3_w pag_w; // number of pages (<= u3a_pages) u3e_image nor_u; // north segment diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 1ad2a27cd..6fe66b48c 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -285,16 +285,7 @@ _ce_image_open(u3e_image* img_u) c3_i mod_i = O_RDWR | O_CREAT; c3_c ful_c[8193]; - snprintf(ful_c, 8192, "%s", u3P.dir_c); - c3_mkdir(ful_c, 0700); - - snprintf(ful_c, 8192, "%s/.urb", u3P.dir_c); - c3_mkdir(ful_c, 0700); - - snprintf(ful_c, 8192, "%s/.urb/chk", u3P.dir_c); - c3_mkdir(ful_c, 0700); - - snprintf(ful_c, 8192, "%s/.urb/chk/%s.bin", u3P.dir_c, img_u->nam_c); + snprintf(ful_c, 8192, "%s/%s.bin", u3P.dir_c, img_u->nam_c); if ( -1 == (img_u->fid_i = c3_open(ful_c, mod_i, 0666)) ) { fprintf(stderr, "loom: c3_open %s: %s\r\n", ful_c, strerror(errno)); return c3n; @@ -381,19 +372,13 @@ _ce_patch_create(u3_ce_patch* pat_u) { c3_c ful_c[8193]; - snprintf(ful_c, 8192, "%s", u3P.dir_c); - c3_mkdir(ful_c, 0700); - - snprintf(ful_c, 8192, "%s/.urb", u3P.dir_c); - c3_mkdir(ful_c, 0700); - - snprintf(ful_c, 8192, "%s/.urb/chk/control.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s/control.bin", u3P.dir_c); if ( -1 == (pat_u->ctl_i = c3_open(ful_c, O_RDWR | O_CREAT | O_EXCL, 0600)) ) { fprintf(stderr, "loom: patch c3_open control.bin: %s\r\n", strerror(errno)); c3_assert(0); } - snprintf(ful_c, 8192, "%s/.urb/chk/memory.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s/memory.bin", u3P.dir_c); if ( -1 == (pat_u->mem_i = c3_open(ful_c, O_RDWR | O_CREAT | O_EXCL, 0600)) ) { fprintf(stderr, "loom: patch c3_open memory.bin: %s\r\n", strerror(errno)); c3_assert(0); @@ -407,13 +392,13 @@ _ce_patch_delete(void) { c3_c ful_c[8193]; - snprintf(ful_c, 8192, "%s/.urb/chk/control.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s/control.bin", u3P.dir_c); if ( unlink(ful_c) ) { fprintf(stderr, "loom: failed to delete control.bin: %s\r\n", strerror(errno)); } - snprintf(ful_c, 8192, "%s/.urb/chk/memory.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s/memory.bin", u3P.dir_c); if ( unlink(ful_c) ) { fprintf(stderr, "loom: failed to remove memory.bin: %s\r\n", strerror(errno)); @@ -489,18 +474,12 @@ _ce_patch_open(void) c3_c ful_c[8193]; c3_i ctl_i, mem_i; - snprintf(ful_c, 8192, "%s", u3P.dir_c); - c3_mkdir(ful_c, 0700); - - snprintf(ful_c, 8192, "%s/.urb", u3P.dir_c); - c3_mkdir(ful_c, 0700); - - snprintf(ful_c, 8192, "%s/.urb/chk/control.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s/control.bin", u3P.dir_c); if ( -1 == (ctl_i = c3_open(ful_c, O_RDWR)) ) { return 0; } - snprintf(ful_c, 8192, "%s/.urb/chk/memory.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s/memory.bin", u3P.dir_c); if ( -1 == (mem_i = c3_open(ful_c, O_RDWR)) ) { close(ctl_i); @@ -1143,7 +1122,9 @@ _ce_backup(void) c3_i mod_i = O_RDWR | O_CREAT; // XX O_TRUNC ? c3_c ful_c[8193]; - snprintf(ful_c, 8192, "%s/.urb/bhk", u3P.dir_c); + // XX move directory creation? + // + snprintf(ful_c, 8192, "%s/.urb/bhk", u3C.dir_c); if ( c3_mkdir(ful_c, 0700) ) { if ( EEXIST != errno ) { @@ -1152,14 +1133,14 @@ _ce_backup(void) return; } - snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3P.dir_c, nop_u.nam_c); + snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3C.dir_c, nop_u.nam_c); if ( -1 == (nop_u.fid_i = c3_open(ful_c, mod_i, 0666)) ) { fprintf(stderr, "loom: c3_open %s: %s\r\n", ful_c, strerror(errno)); return; } - snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3P.dir_c, sop_u.nam_c); + snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3C.dir_c, sop_u.nam_c); if ( -1 == (sop_u.fid_i = c3_open(ful_c, mod_i, 0666)) ) { fprintf(stderr, "loom: c3_open %s: %s\r\n", ful_c, strerror(errno)); @@ -1171,9 +1152,9 @@ _ce_backup(void) { c3_unlink(ful_c); - snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3P.dir_c, nop_u.nam_c); + snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3C.dir_c, nop_u.nam_c); c3_unlink(ful_c); - snprintf(ful_c, 8192, "%s/.urb/bhk", u3P.dir_c); + snprintf(ful_c, 8192, "%s/.urb/bhk", u3C.dir_c); c3_rmdir(ful_c); } diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 65f0cd4b9..bceaa9f88 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -1875,7 +1875,35 @@ u3m_boot(c3_c* dir_c, size_t len_i) /* Activate the storage system. */ - nuu_o = u3e_live(c3n, dir_c); + { + c3_c ful_c[8193]; + + snprintf(ful_c, 8192, "%s", dir_c); + if ( c3_mkdir(ful_c, 0700) ) { + if ( EEXIST != errno ) { + fprintf(stderr, "loom: pier create: %s\r\n", strerror(errno)); + c3_assert(0); + } + } + + snprintf(ful_c, 8192, "%s/.urb", dir_c); + if ( c3_mkdir(ful_c, 0700) ) { + if ( EEXIST != errno ) { + fprintf(stderr, "loom: .urb create: %s\r\n", strerror(errno)); + c3_assert(0); + } + } + + snprintf(ful_c, 8192, "%s/.urb/chk", dir_c); + if ( c3_mkdir(ful_c, 0700) ) { + if ( EEXIST != errno ) { + fprintf(stderr, "loom: .urb/chk create: %s\r\n", strerror(errno)); + c3_assert(0); + } + } + + nuu_o = u3e_live(c3n, strdup(ful_c)); + } /* Activate tracing. */ From f5fdaa27817172f7d2b856830c8755f24548de06 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 7 Dec 2022 17:33:48 -0500 Subject: [PATCH 077/136] u3: automatically place the guard page in u3e_live() --- pkg/urbit/noun/events.c | 4 ++++ pkg/urbit/noun/manage.c | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 6fe66b48c..aff72fb22 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -1339,6 +1339,10 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) u3a_print_memory(stderr, "live: mapped", nor_w << u3a_page); u3a_print_memory(stderr, "live: loaded", sou_w << u3a_page); } + +#ifdef U3_GUARD_PAGE + _ce_center_guard_page(); +#endif } } diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index bceaa9f88..866937990 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -1916,10 +1916,6 @@ u3m_boot(c3_c* dir_c, size_t len_i) */ u3m_pave(nuu_o); - /* Place the guard page. - */ - u3e_init(); - /* Initialize the jet system. */ { From 5726f08da844210fe5f28e9ea7c80312abd84c89 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 7 Dec 2022 17:45:23 -0500 Subject: [PATCH 078/136] u3: simplifies road stack adjustment after snapshot load --- pkg/urbit/noun/manage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 866937990..12dd9e45f 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -594,7 +594,7 @@ _find_home(void) // this looks risky, but there are no legitimate scenarios // where it's wrong // - u3R->cap_p = u3R->mat_p = u3C.wor_i - c3_wiseof(*u3H); + u3R->cap_p = u3R->mat_p = u3a_outa(u3H); } /* u3m_pave(): instantiate or activate image. From a8fc001cdf1a200b05a82f36b11147ffb9538cfb Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 12 Dec 2022 04:55:08 -0330 Subject: [PATCH 079/136] meta: add ota action --- .github/actions/ota/Dockerfile | 4 ++++ .github/actions/ota/action.yml | 28 ++++++++++++++++++++++++++++ .github/actions/ota/entrypoint.sh | 20 ++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 .github/actions/ota/Dockerfile create mode 100644 .github/actions/ota/action.yml create mode 100755 .github/actions/ota/entrypoint.sh diff --git a/.github/actions/ota/Dockerfile b/.github/actions/ota/Dockerfile new file mode 100644 index 000000000..bee188917 --- /dev/null +++ b/.github/actions/ota/Dockerfile @@ -0,0 +1,4 @@ +FROM tloncorp/janeway:v0.17.0 +COPY entrypoint.sh /entrypoint.sh +EXPOSE 22/tcp +ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/actions/ota/action.yml b/.github/actions/ota/action.yml new file mode 100644 index 000000000..f9b0d027e --- /dev/null +++ b/.github/actions/ota/action.yml @@ -0,0 +1,28 @@ +name: 'ota' +description: 'perform an OTA update of arvo on a remote ship' +inputs: + ship: + description: "target ship" + required: true + credentials: + description: "base64-encoded GCP Service Account credentials" + required: true + ssh-sec-key: + description: "base64-encoded SSH secret key for the container to use" + required: true + ssh-pub-key: + description: "base64-encoded corresponding SSH public key" + required: true + ref: + description: "git ref of arvo source to check out" + required: false + +runs: + using: 'docker' + image: 'Dockerfile' + args: + - ${{ inputs.ship }} + - ${{ inputs.credentials }} + - ${{ inputs.ssh-sec-key }} + - ${{ inputs.ssh-pub-key }} + - ${{ inputs.ref }} diff --git a/.github/actions/ota/entrypoint.sh b/.github/actions/ota/entrypoint.sh new file mode 100755 index 000000000..ec34000b2 --- /dev/null +++ b/.github/actions/ota/entrypoint.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +echo "$2" | base64 -d > /service-account +echo "$3" | base64 -d > /id_ssh +echo "$4" | base64 -d > /id_ssh.pub + +chmod 600 /service-account +chmod 600 /id_ssh +chmod 600 /id_ssh.pub + +janeway \ + --ci \ + --verbose \ + --credentials /service-account \ + --ssh-key /id_ssh \ + release ota --no-commit \ # XX remove flag after testing + arvo \ + "$1" \ + ${5:+"--ref"} ${5:+"$5"} \ + | bash From 4f6ca764cce3ada72a418d0b6989a7a128a13c5f Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 12 Dec 2022 05:04:05 -0330 Subject: [PATCH 080/136] meta: add ota workflow --- .github/workflows/ota.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/ota.yml diff --git a/.github/workflows/ota.yml b/.github/workflows/ota.yml new file mode 100644 index 000000000..fbb46592a --- /dev/null +++ b/.github/workflows/ota.yml @@ -0,0 +1,19 @@ +name: ota +on: + workflow_dispatch: + push: + branches: + - 'next/arvo' +jobs: + deploy: + runs-on: ubuntu-latest + name: "make an OTA update to arvo on ~wannec-dozzod-marzod (devstream)" + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/ota + with: + ship: 'devstream' + credentials: ${{ secrets.JANEWAY_SERVICE_KEY }} + ssh-sec-key: ${{ secrets.JANEWAY_SSH_SEC_KEY }} + ssh-pub-key: ${{ secrets.JANEWAY_SSH_PUB_KEY }} + ref: 'next/arvo' From 882b82b5ea24d1fd9285a3ad9ff360fad125265b Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 12 Dec 2022 05:07:12 -0330 Subject: [PATCH 081/136] meta: target jt/merge-ota for testing --- .github/workflows/ota.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ota.yml b/.github/workflows/ota.yml index fbb46592a..1a738c242 100644 --- a/.github/workflows/ota.yml +++ b/.github/workflows/ota.yml @@ -3,7 +3,7 @@ on: workflow_dispatch: push: branches: - - 'next/arvo' + - 'jt/merge-ota' # XX next/arvo jobs: deploy: runs-on: ubuntu-latest From 0631b679071e64383a5795fd42b9f5a08e1f107a Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 12 Dec 2022 05:15:23 -0330 Subject: [PATCH 082/136] meta: remove breaking comment --- .github/actions/ota/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/ota/entrypoint.sh b/.github/actions/ota/entrypoint.sh index ec34000b2..ba16ce370 100755 --- a/.github/actions/ota/entrypoint.sh +++ b/.github/actions/ota/entrypoint.sh @@ -13,7 +13,7 @@ janeway \ --verbose \ --credentials /service-account \ --ssh-key /id_ssh \ - release ota --no-commit \ # XX remove flag after testing + release ota --no-commit \ arvo \ "$1" \ ${5:+"--ref"} ${5:+"$5"} \ From 6764130504cd4d12b440b98aa5a07e64cdcaa935 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 12 Dec 2022 05:20:48 -0330 Subject: [PATCH 083/136] meta: give ota workflow live args --- .github/actions/ota/entrypoint.sh | 2 +- .github/workflows/ota.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/ota/entrypoint.sh b/.github/actions/ota/entrypoint.sh index ba16ce370..5839a49f4 100755 --- a/.github/actions/ota/entrypoint.sh +++ b/.github/actions/ota/entrypoint.sh @@ -13,7 +13,7 @@ janeway \ --verbose \ --credentials /service-account \ --ssh-key /id_ssh \ - release ota --no-commit \ + release ota \ arvo \ "$1" \ ${5:+"--ref"} ${5:+"$5"} \ diff --git a/.github/workflows/ota.yml b/.github/workflows/ota.yml index 1a738c242..fbb46592a 100644 --- a/.github/workflows/ota.yml +++ b/.github/workflows/ota.yml @@ -3,7 +3,7 @@ on: workflow_dispatch: push: branches: - - 'jt/merge-ota' # XX next/arvo + - 'next/arvo' jobs: deploy: runs-on: ubuntu-latest From 716281cb0ddce0132a2dff971e84de9d96214bf4 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 12 Dec 2022 13:51:10 -0330 Subject: [PATCH 084/136] meta: target ota workflow at ~binnec-dozzod-marzod --- .github/workflows/ota.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ota.yml b/.github/workflows/ota.yml index fbb46592a..d4742e478 100644 --- a/.github/workflows/ota.yml +++ b/.github/workflows/ota.yml @@ -7,12 +7,12 @@ on: jobs: deploy: runs-on: ubuntu-latest - name: "make an OTA update to arvo on ~wannec-dozzod-marzod (devstream)" + name: "make an OTA update to arvo on ~binnec-dozzod-marzod" steps: - uses: actions/checkout@v3 - uses: ./.github/actions/ota with: - ship: 'devstream' + ship: 'canary' credentials: ${{ secrets.JANEWAY_SERVICE_KEY }} ssh-sec-key: ${{ secrets.JANEWAY_SSH_SEC_KEY }} ssh-pub-key: ${{ secrets.JANEWAY_SSH_PUB_KEY }} From e1419d1787ae551999dd8c4e123115c0b62a4a06 Mon Sep 17 00:00:00 2001 From: Philip Monk Date: Mon, 12 Dec 2022 13:03:41 -0700 Subject: [PATCH 085/136] contact-store: no-op if adding old contact %contact-store is responsible for sending updates about contacts, eg profile color. When it hears an update, it fans that out to its subsribers, unless that update is stale. If you reguarly fan out stale updates, then they reverberate across the network indefinitely -- we call this "echoing". To cut off this echoing, all edits have a timestamp, and we consider any updates from before this timestamp to be stale. Additions are separate from edits, and for them we instead do a value comparison on the contact -- if it didn't change, we consider the update stale. The problem with this scheme is that if an addition and edit happen one after the other in quick succession, you might have the following sequence: - add comes in with timestamp T1 - edit comes in with timestamp T2 after T1 - we hear an echo of the add, and that errantly applies because it passes our "did the contact actually change" check - we hear an echo of the edit, which applies because T2 is after T1 - GOTO 3 Each time we apply the stale update, we fan that out to our subscribers, and if any two hosts subscribe to each other, this will loop. This may even loop unconditionally because the ship that made the profile changes seems like it might not recognize that those changes didn't come from itself, so it sends them to all the groups it's in. If so, that's an important issue to fix. Fixes tloncorp/landscape-issues#1442 --- bin/multi-brass.pill | 4 ++-- pkg/landscape/app/contact-store.hoon | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/bin/multi-brass.pill b/bin/multi-brass.pill index e1229bb75..d95c40bd6 100644 --- a/bin/multi-brass.pill +++ b/bin/multi-brass.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3306a5b916caa838c67cb079410c6e5bb158054520129ffb9f9bdb144ab1b691 -size 7418493 +oid sha256:ea8626444e4f0213e39c21ded20607145ee85a947afc592f182f46e7f598ef30 +size 7748671 diff --git a/pkg/landscape/app/contact-store.hoon b/pkg/landscape/app/contact-store.hoon index e3f750715..bac7cd9ec 100644 --- a/pkg/landscape/app/contact-store.hoon +++ b/pkg/landscape/app/contact-store.hoon @@ -123,7 +123,15 @@ :: ensure difference =/ old=(unit contact:store) (~(get by rolodex) ship) ?. ?| ?=(~ old) - !=(contact(last-updated *@da) u.old(last-updated *@da)) + :: if new contact is before existing contact, no-op + :: + :: NB: last-updated.contact is often *@da, so this + :: effectively stops add from applying if we already have + :: a contact for them + :: + ?& (gth last-updated.contact last-updated.u.old) + !=(contact(last-updated *@da) u.old(last-updated *@da)) + == == [~ state] ~| "cannot add a data url to cover!" From 12229a50e8749818b16f33ea064990231c4e42b6 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 7 Dec 2022 19:06:40 -0500 Subject: [PATCH 086/136] u3: adds snapshot facade to manage.c --- pkg/urbit/bench/ur_bench.c | 4 +--- pkg/urbit/compat/mingw/seh_handler.c | 2 +- pkg/urbit/daemon/main.c | 8 ++++---- pkg/urbit/include/all.h | 1 - pkg/urbit/include/noun/events.h | 2 +- pkg/urbit/include/noun/manage.h | 10 ++++++++++ pkg/urbit/noun/events.c | 1 + pkg/urbit/noun/manage.c | 19 ++++++++++++++++++- pkg/urbit/noun/urth.c | 1 + pkg/urbit/tests/ames_tests.c | 3 +-- pkg/urbit/tests/events_tests.c | 1 + pkg/urbit/tests/hashtable_tests.c | 3 +-- pkg/urbit/tests/jam_tests.c | 4 +--- pkg/urbit/tests/jet_tests.c | 3 +-- pkg/urbit/tests/meme_tests.c | 4 +--- pkg/urbit/tests/mug_tests.c | 3 +-- pkg/urbit/tests/newt_tests.c | 3 +-- pkg/urbit/tests/noun_tests.c | 3 +-- pkg/urbit/worker/serf.c | 4 ++-- 19 files changed, 48 insertions(+), 31 deletions(-) diff --git a/pkg/urbit/bench/ur_bench.c b/pkg/urbit/bench/ur_bench.c index 4c0151357..91f04bbbf 100644 --- a/pkg/urbit/bench/ur_bench.c +++ b/pkg/urbit/bench/ur_bench.c @@ -7,9 +7,7 @@ static void _setup(void) { - u3m_init(1 << 24); - u3m_pave(c3y); - u3e_init(); + u3m_boot_lite(1 << 24); } /* _ames_writ_ex(): |hi packet from fake ~zod to fake ~nec diff --git a/pkg/urbit/compat/mingw/seh_handler.c b/pkg/urbit/compat/mingw/seh_handler.c index ecf59f0a4..6730435e4 100644 --- a/pkg/urbit/compat/mingw/seh_handler.c +++ b/pkg/urbit/compat/mingw/seh_handler.c @@ -12,7 +12,7 @@ EXCEPTION_DISPOSITION _mingw_exception_filter( { if (ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && ExceptionRecord->ExceptionInformation[0] == 1 && - u3e_fault((void*)ExceptionRecord->ExceptionInformation[1], 1)) + u3m_fault((void*)ExceptionRecord->ExceptionInformation[1], 1)) { return ExceptionContinueExecution; } diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index 53169ec8c..183a1ae98 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -1452,7 +1452,7 @@ _cw_cram(c3_i argc, c3_c* argv[]) // save even on failure, as we just did all the work of deduplication // - u3e_save(); + u3m_save(); u3_disk_exit(log_u); if ( c3n == ret_o ) { @@ -1545,7 +1545,7 @@ _cw_queu(c3_i argc, c3_c* argv[]) exit(1); } - u3e_save(); + u3m_save(); u3_disk_exit(log_u); fprintf(stderr, "urbit: queu: rock loaded at event %" PRIu64 "\r\n", eve_d); @@ -1623,7 +1623,7 @@ _cw_meld(c3_i argc, c3_c* argv[]) u3u_meld(); u3a_print_memory(stderr, "urbit: meld: gained", (u3a_open(u3R) - pre_w)); - u3e_save(); + u3m_save(); u3_disk_exit(log_u); u3m_stop(); } @@ -1763,7 +1763,7 @@ _cw_pack(c3_i argc, c3_c* argv[]) u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); u3a_print_memory(stderr, "urbit: pack: gained", u3m_pack()); - u3e_save(); + u3m_save(); u3_disk_exit(log_u); u3m_stop(); } diff --git a/pkg/urbit/include/all.h b/pkg/urbit/include/all.h index ae647d865..402b88583 100644 --- a/pkg/urbit/include/all.h +++ b/pkg/urbit/include/all.h @@ -11,7 +11,6 @@ # include "noun/aliases.h" // general u3 # include "noun/allocate.h" // u3a: allocation -# include "noun/events.h" // u3e: persistence # include "noun/hashtable.h" // u3h: hashtables # include "noun/imprison.h" // u3i: noun construction # include "noun/jets.h" // u3j: jet control diff --git a/pkg/urbit/include/noun/events.h b/pkg/urbit/include/noun/events.h index e0bd58123..14ddd3d64 100644 --- a/pkg/urbit/include/noun/events.h +++ b/pkg/urbit/include/noun/events.h @@ -65,7 +65,7 @@ c3_i u3e_fault(void* adr_v, c3_i ser_i); - /* u3e_save(): + /* u3e_save(): update the checkpoint. */ void u3e_save(void); diff --git a/pkg/urbit/include/noun/manage.h b/pkg/urbit/include/noun/manage.h index 72e29f533..0c2348573 100644 --- a/pkg/urbit/include/noun/manage.h +++ b/pkg/urbit/include/noun/manage.h @@ -34,6 +34,16 @@ c3_i u3m_bail(c3_m how_m) __attribute__((noreturn)); + /* u3m_fault(): handle a memory event with libsigsegv protocol. + */ + c3_i + u3m_fault(void* adr_v, c3_i ser_i); + + /* u3m_save(): update the checkpoint. + */ + void + u3m_save(void); + /* u3m_init(): start the environment. */ void diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index aff72fb22..ad2e9ddde 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -91,6 +91,7 @@ //! #include "all.h" +#include "noun/events.h" #include #include #include diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 12dd9e45f..baab541de 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -2,6 +2,7 @@ ** */ #include "all.h" +#include "noun/events.h" #include "rsignal.h" #include "vere/vere.h" #include @@ -1686,6 +1687,22 @@ _cm_limits(void) # endif } +/* u3m_fault(): handle a memory event with libsigsegv protocol. +*/ +c3_i +u3m_fault(void* adr_v, c3_i ser_i) +{ + return u3e_fault(adr_v, ser_i); +} + +/* u3m_save(): update the checkpoint. +*/ +void +u3m_save(void) +{ + return u3e_save(); +} + /* _cm_signals(): set up interrupts, etc. */ static void @@ -1701,7 +1718,7 @@ _cm_signals(void) // filter (see compat/mingw/seh_handler.c) that handles both memory // access and stack overflow exceptions. It calls u3e_fault directly. # else - if ( 0 != sigsegv_install_handler(u3e_fault) ) { + if ( 0 != sigsegv_install_handler(u3m_fault) ) { u3l_log("boot: sigsegv install failed\n"); exit(1); } diff --git a/pkg/urbit/noun/urth.c b/pkg/urbit/noun/urth.c index 009442e9e..22163e9dd 100644 --- a/pkg/urbit/noun/urth.c +++ b/pkg/urbit/noun/urth.c @@ -2,6 +2,7 @@ ** */ #include "all.h" +#include "noun/events.h" #include "ur/ur.h" #include #include diff --git a/pkg/urbit/tests/ames_tests.c b/pkg/urbit/tests/ames_tests.c index 0907898d4..38581d8ca 100644 --- a/pkg/urbit/tests/ames_tests.c +++ b/pkg/urbit/tests/ames_tests.c @@ -6,8 +6,7 @@ static void _setup(void) { - u3m_init(1 << 22); - u3m_pave(c3y); + u3m_boot_lite(1 << 22); } /* _test_ames(): spot check ames helpers diff --git a/pkg/urbit/tests/events_tests.c b/pkg/urbit/tests/events_tests.c index f42ce5625..92f1bd2fe 100644 --- a/pkg/urbit/tests/events_tests.c +++ b/pkg/urbit/tests/events_tests.c @@ -1,4 +1,5 @@ #include "all.h" +#include "noun/events.h" /* _setup(): prepare for tests. */ diff --git a/pkg/urbit/tests/hashtable_tests.c b/pkg/urbit/tests/hashtable_tests.c index 3b36dc887..051ec709e 100644 --- a/pkg/urbit/tests/hashtable_tests.c +++ b/pkg/urbit/tests/hashtable_tests.c @@ -9,8 +9,7 @@ c3_w _ch_skip_slot(c3_w mug_w, c3_w lef_w); static void _setup(void) { - u3m_init(1 << 26); - u3m_pave(c3y); + u3m_boot_lite(1 << 26); } /* _test_bit_manipulation(): diff --git a/pkg/urbit/tests/jam_tests.c b/pkg/urbit/tests/jam_tests.c index fd416945d..d32796855 100644 --- a/pkg/urbit/tests/jam_tests.c +++ b/pkg/urbit/tests/jam_tests.c @@ -6,9 +6,7 @@ static void _setup(void) { - u3m_init(1 << 24); - u3m_pave(c3y); - u3e_init(); + u3m_boot_lite(1 << 24); } static void diff --git a/pkg/urbit/tests/jet_tests.c b/pkg/urbit/tests/jet_tests.c index 4904a959d..d88d55e93 100644 --- a/pkg/urbit/tests/jet_tests.c +++ b/pkg/urbit/tests/jet_tests.c @@ -5,8 +5,7 @@ static void _setup(void) { - u3m_init(1 << 20); - u3m_pave(c3y); + u3m_boot_lite(1 << 20); } static inline c3_i diff --git a/pkg/urbit/tests/meme_tests.c b/pkg/urbit/tests/meme_tests.c index e52e4c028..751abb985 100644 --- a/pkg/urbit/tests/meme_tests.c +++ b/pkg/urbit/tests/meme_tests.c @@ -5,9 +5,7 @@ static void _setup(void) { - u3m_init(1 << 24); - u3m_pave(c3y); - u3e_init(); + u3m_boot_lite(1 << 24); } static u3_noun diff --git a/pkg/urbit/tests/mug_tests.c b/pkg/urbit/tests/mug_tests.c index ffd051674..1c9dcddbc 100644 --- a/pkg/urbit/tests/mug_tests.c +++ b/pkg/urbit/tests/mug_tests.c @@ -5,8 +5,7 @@ static void _setup(void) { - u3m_init(1 << 20); - u3m_pave(c3y); + u3m_boot_lite(1 << 20); } /* _test_mug(): spot check u3r_mug hashes. diff --git a/pkg/urbit/tests/newt_tests.c b/pkg/urbit/tests/newt_tests.c index 6fdbdb759..75c0a07b1 100644 --- a/pkg/urbit/tests/newt_tests.c +++ b/pkg/urbit/tests/newt_tests.c @@ -6,8 +6,7 @@ static void _setup(void) { - u3m_init(1 << 20); - u3m_pave(c3y); + u3m_boot_lite(1 << 20); } /* _newt_encode(): synchronous serialization into a single buffer, for test purposes diff --git a/pkg/urbit/tests/noun_tests.c b/pkg/urbit/tests/noun_tests.c index af54cd289..1f0527bd5 100644 --- a/pkg/urbit/tests/noun_tests.c +++ b/pkg/urbit/tests/noun_tests.c @@ -8,8 +8,7 @@ static void _setup(void) { - u3m_init(1 << 20); - u3m_pave(c3y); + u3m_boot_lite(1 << 20); } /* _test_u3r_chop: "extract bit slices from atom" diff --git a/pkg/urbit/worker/serf.c b/pkg/urbit/worker/serf.c index f48b6f291..4728895b7 100644 --- a/pkg/urbit/worker/serf.c +++ b/pkg/urbit/worker/serf.c @@ -883,7 +883,7 @@ _serf_writ_live_save(u3_serf* sef_u, c3_d eve_d) exit(1); } - u3e_save(); + u3m_save(); } /* u3_serf_live(): apply %live command [com], producing *ret on c3y. @@ -954,7 +954,7 @@ u3_serf_live(u3_serf* sef_u, u3_noun com, u3_noun* ret) return c3n; } - u3e_save(); + u3m_save(); u3_serf_grab(); *ret = u3nc(c3__live, u3_nul); From 54e591b7191b505ece55d0d62fd56966348a24e9 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 7 Dec 2022 19:17:02 -0500 Subject: [PATCH 087/136] u3: simplifies u3e_live() signature --- pkg/urbit/include/noun/events.h | 2 +- pkg/urbit/noun/events.c | 4 +++- pkg/urbit/noun/manage.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/urbit/include/noun/events.h b/pkg/urbit/include/noun/events.h index 14ddd3d64..84686467d 100644 --- a/pkg/urbit/include/noun/events.h +++ b/pkg/urbit/include/noun/events.h @@ -73,7 +73,7 @@ /* u3e_live(): start the persistence system. Return c3y if no image. */ c3_o - u3e_live(c3_o nuu_o, c3_c* dir_c); + u3e_live(c3_c* dir_c); /* u3e_yolo(): disable dirty page tracking, read/write whole loom. */ diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index ad2e9ddde..e9ca30e13 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -1243,8 +1243,10 @@ u3e_save(void) /* u3e_live(): start the checkpointing system. */ c3_o -u3e_live(c3_o nuu_o, c3_c* dir_c) +u3e_live(c3_c* dir_c) { + c3_o nuu_o = c3n; + // XX demand paging is not supported on windows // #ifdef U3_OS_mingw diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index baab541de..d2795ddf0 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -1919,7 +1919,7 @@ u3m_boot(c3_c* dir_c, size_t len_i) } } - nuu_o = u3e_live(c3n, strdup(ful_c)); + nuu_o = u3e_live(strdup(ful_c)); } /* Activate tracing. From e378b497cb871da93f07acd0fe9fabbe96d07bbd Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 9 Dec 2022 23:45:14 -0500 Subject: [PATCH 088/136] u3: fixes compile-time conditionals around guard page impl --- pkg/urbit/noun/events.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index e9ca30e13..8b108df0a 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -840,6 +840,7 @@ _ce_loom_protect_north(c3_w pgs_w, c3_w old_w) c3_assert(0); } +#ifdef U3_GUARD_PAGE // protect guard page if clobbered // // NB: < pgs_w is precluded by assertion in _ce_patch_compose() @@ -852,6 +853,7 @@ _ce_loom_protect_north(c3_w pgs_w, c3_w old_w) c3_assert(0); } } +#endif _ce_loom_track_north(pgs_w, dif_w); } @@ -888,6 +890,7 @@ _ce_loom_protect_south(c3_w pgs_w, c3_w old_w) c3_assert(0); } +#ifdef U3_GUARD_PAGE // protect guard page if clobbered // // NB: > pgs_w is precluded by assertion in _ce_patch_compose() @@ -900,6 +903,7 @@ _ce_loom_protect_south(c3_w pgs_w, c3_w old_w) c3_assert(0); } } +#endif _ce_loom_track_south(pgs_w, dif_w); } @@ -949,6 +953,7 @@ _ce_loom_mapf_north(c3_i fid_i, c3_w pgs_w, c3_w old_w) c3_assert(0); } +#ifdef U3_GUARD_PAGE // protect guard page if clobbered // // NB: < pgs_w is precluded by assertion in _ce_patch_compose() @@ -961,6 +966,7 @@ _ce_loom_mapf_north(c3_i fid_i, c3_w pgs_w, c3_w old_w) c3_assert(0); } } +#endif _ce_loom_track_north(pgs_w, dif_w); } @@ -1369,11 +1375,13 @@ u3e_yolo(void) return c3n; } +#ifdef U3_GUARD_PAGE if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { fprintf(stderr, "loom: failed to protect guard page: %s\r\n", strerror(errno)); c3_assert(0); } +#endif // mark all pages dirty // From 9fe6f3dde04f7864262ba257a89532856b6571af Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 9 Dec 2022 23:44:51 -0500 Subject: [PATCH 089/136] u3: refactors loom protect-south page-length calculation blah --- pkg/urbit/noun/events.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 8b108df0a..16065326b 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -880,8 +880,9 @@ _ce_loom_protect_south(c3_w pgs_w, c3_w old_w) if ( old_w > pgs_w ) { c3_w dif_w = old_w - pgs_w; + c3_w off_w = u3P.pag_w - old_w; - if ( 0 != mprotect((void*)(u3_Loom + ((u3P.pag_w - old_w) << u3a_page)), + if ( 0 != mprotect((void*)(u3_Loom + (off_w << u3a_page)), (size_t)dif_w << (u3a_page + 2), (PROT_READ | PROT_WRITE)) ) { @@ -893,9 +894,9 @@ _ce_loom_protect_south(c3_w pgs_w, c3_w old_w) #ifdef U3_GUARD_PAGE // protect guard page if clobbered // - // NB: > pgs_w is precluded by assertion in _ce_patch_compose() + // NB: >= pgs_w is precluded by assertion in _ce_patch_compose() // - if ( (gar_pag_p >> u3a_page) >= (u3a_pages - (old_w + 1)) ) { + if ( (gar_pag_p >> u3a_page) >= off_w ) { fprintf(stderr, "loom: guard on reprotect\r\n"); if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { fprintf(stderr, "loom: failed to protect guard page: %s\r\n", From 4659b92d26f08925d9c5a27900a933fbaca53cb8 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Mon, 12 Dec 2022 20:50:34 -0500 Subject: [PATCH 090/136] u3: factors out individual page protections --- pkg/urbit/noun/events.c | 80 ++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 16065326b..641ddf3aa 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -167,7 +167,38 @@ u3e_check(c3_c* cap_c) } #endif +/* _ce_flaw_protect(): protect page after fault. +*/ +static inline c3_i +_ce_flaw_protect(c3_w pag_w) +{ + if ( 0 != mprotect((void *)(u3_Loom + (pag_w << u3a_page)), + pag_siz_i, + (PROT_READ | PROT_WRITE)) ) + { + fprintf(stderr, "loom: fault mprotect (%u): %s\r\n", + pag_w, strerror(errno)); + return 1; + } + + return 0; +} + #ifdef U3_GUARD_PAGE +/* _ce_ward_protect(): protect the guard page. +*/ +static inline c3_i +_ce_ward_protect(void) +{ + if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { + fprintf(stderr, "loom: failed to protect guard page (%u): %s\r\n", + gar_pag_p >> u3a_page, strerror(errno)); + return 1; + } + + return 0; +} + //! Place a guard page at the (approximate) middle of the free space between //! the heap and stack of the current road, bailing if memory has been //! exhausted. @@ -204,12 +235,7 @@ _ce_center_guard_page(void) goto bail; } - if ( -1 == mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { - fprintf(stderr, - "loom: failed to protect the guard page " - "(base address %p): %s\r\n", - u3a_into(gar_pag_p), - strerror(errno)); + if ( _ce_ward_protect() ) { goto fail; } @@ -266,11 +292,7 @@ u3e_fault(void* adr_v, c3_i ser_i) u3P.dit_w[blk_w] |= (1 << bit_w); - if ( -1 == mprotect((void *)(u3_Loom + (pag_w << u3a_page)), - pag_siz_i, - (PROT_READ | PROT_WRITE)) ) - { - fprintf(stderr, "loom: fault mprotect: %s\r\n", strerror(errno)); + if ( _ce_flaw_protect(pag_w) ) { c3_assert(0); return 0; } @@ -847,11 +869,7 @@ _ce_loom_protect_north(c3_w pgs_w, c3_w old_w) // if ( (gar_pag_p >> u3a_page) < old_w ) { fprintf(stderr, "loom: guard on reprotect\r\n"); - if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { - fprintf(stderr, "loom: failed to protect guard page: %s\r\n", - strerror(errno)); - c3_assert(0); - } + c3_assert( !_ce_ward_protect() ); } #endif @@ -898,11 +916,7 @@ _ce_loom_protect_south(c3_w pgs_w, c3_w old_w) // if ( (gar_pag_p >> u3a_page) >= off_w ) { fprintf(stderr, "loom: guard on reprotect\r\n"); - if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { - fprintf(stderr, "loom: failed to protect guard page: %s\r\n", - strerror(errno)); - c3_assert(0); - } + c3_assert( !_ce_ward_protect() ); } #endif @@ -961,11 +975,7 @@ _ce_loom_mapf_north(c3_i fid_i, c3_w pgs_w, c3_w old_w) // if ( (gar_pag_p >> u3a_page) < old_w ) { fprintf(stderr, "loom: guard on remap\r\n"); - if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { - fprintf(stderr, "loom: failed to protect guard page: %s\r\n", - strerror(errno)); - c3_assert(0); - } + c3_assert( !_ce_ward_protect() ); } #endif @@ -1377,11 +1387,7 @@ u3e_yolo(void) } #ifdef U3_GUARD_PAGE - if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { - fprintf(stderr, "loom: failed to protect guard page: %s\r\n", - strerror(errno)); - c3_assert(0); - } + c3_assert( !_ce_ward_protect() ); #endif // mark all pages dirty @@ -1401,6 +1407,8 @@ u3e_init(void) #ifdef U3_GUARD_PAGE _ce_center_guard_page(); #endif + + _ce_loom_track_north(0, u3P.pag_w); } /* u3e_ward(): reposition guard page if needed. @@ -1413,15 +1421,7 @@ u3e_ward(u3_post low_p, u3_post hig_p) if ( (low_p > gar_p) || (hig_p < gar_p) ) { _ce_center_guard_page(); - - if ( 0 != mprotect(u3a_into(gar_p), - pag_siz_i, - (PROT_READ | PROT_WRITE)) ) - { - fprintf(stderr, "loom: failed to unprotect old guard page: %s\r\n", - strerror(errno)); - c3_assert(0); - } + c3_assert( !_ce_flaw_protect(gar_p >> u3a_page) ); { c3_w pag_w = gar_p >> u3a_page; From d043a42128ca32ad712a24758ea09e2ba7d0eae3 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Mon, 12 Dec 2022 21:49:36 -0500 Subject: [PATCH 091/136] u3: reimplements guard page w/out posts --- pkg/urbit/include/noun/events.h | 1 + pkg/urbit/noun/events.c | 142 +++++++++++++++++--------------- 2 files changed, 76 insertions(+), 67 deletions(-) diff --git a/pkg/urbit/include/noun/events.h b/pkg/urbit/include/noun/events.h index 84686467d..08711dda5 100644 --- a/pkg/urbit/include/noun/events.h +++ b/pkg/urbit/include/noun/events.h @@ -42,6 +42,7 @@ c3_c* dir_c; // checkpoint dir c3_w dit_w[u3a_pages >> 5]; // touched since last save c3_w pag_w; // number of pages (<= u3a_pages) + c3_w gar_w; // guard page u3e_image nor_u; // north segment u3e_image sou_u; // south segment } u3e_pool; diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 641ddf3aa..aaaf4e278 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -55,7 +55,7 @@ //! definitions: //! - a clean page is PROT_READ and 0 in the bitmap //! - a dirty page is (PROT_READ|PROT_WRITE) and 1 in the bitmap -//! - the guard page is PROT_NONE and 1 in the bitmap (XX assumed) +//! - the guard page is PROT_NONE and 1 in the bitmap //! //! assumptions: //! - all memory access patterns are outside-in, a page at a time @@ -96,9 +96,6 @@ #include #include -// Base loom offset of the guard page. -static u3p(c3_w) gar_pag_p; - //! Urbit page size in 4-byte words. #define pag_wiz_i ((size_t)1 << u3a_page) @@ -190,61 +187,71 @@ _ce_flaw_protect(c3_w pag_w) static inline c3_i _ce_ward_protect(void) { - if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { + if ( 0 != mprotect((void *)(u3_Loom + (u3P.gar_w << u3a_page)), + pag_siz_i, + PROT_NONE) ) + { fprintf(stderr, "loom: failed to protect guard page (%u): %s\r\n", - gar_pag_p >> u3a_page, strerror(errno)); + u3P.gar_w, strerror(errno)); return 1; } return 0; } -//! Place a guard page at the (approximate) middle of the free space between -//! the heap and stack of the current road, bailing if memory has been -//! exhausted. -static c3_i -_ce_center_guard_page(void) +/* _ce_ward_post(): set the guard page. +*/ +static inline c3_i +_ce_ward_post(c3_w nop_w, c3_w sop_w) { - u3p(c3_w) bot_p, top_p; - if ( !u3R ) { - top_p = u3a_outa(u3_Loom + u3C.wor_i); - bot_p = u3a_outa(u3_Loom); - } - else if ( c3y == u3a_is_north(u3R) ) { - top_p = c3_rod(u3R->cap_p, pag_wiz_i); - bot_p = c3_rop(u3R->hat_p, pag_wiz_i); - } - else { - top_p = c3_rod(u3R->hat_p, pag_wiz_i); - bot_p = c3_rop(u3R->cap_p, pag_wiz_i); + u3P.gar_w = nop_w + ((sop_w - nop_w) / 2); + return _ce_ward_protect(); +} + +/* _ce_ward_clip(): hit the guard page. +*/ +static inline c3_i +_ce_ward_clip(void) +{ + const c3_w old_w = u3P.gar_w; + c3_w nop_w, sop_w; + { + u3_post low_p, hig_p; + + if ( !u3R ) { + low_p = 1; + hig_p = u3C.wor_i - 1; + } + else if ( c3y == u3a_is_north(u3R) ) { + low_p = u3R->hat_p; + hig_p = u3R->cap_p; + } + else { + low_p = u3R->cap_p; + hig_p = u3R->hat_p; + } + + nop_w = (low_p - 1) >> u3a_page; + sop_w = hig_p >> u3a_page; } - if ( top_p < bot_p + pag_wiz_i ) { - fprintf(stderr, - "loom: not enough memory to recenter the guard page\r\n"); - goto bail; - } - const u3p(c3_w) old_gar_p = gar_pag_p; - const c3_w mid_p = (top_p - bot_p) / 2; - gar_pag_p = bot_p + c3_rod(mid_p, pag_wiz_i); - if ( old_gar_p == gar_pag_p ) { - fprintf(stderr, - "loom: can't move the guard page to the same location" - " (base address %p)\r\n", - u3a_into(gar_pag_p)); - goto bail; + if ( !u3P.gar_w || ((nop_w < u3P.gar_w) && (sop_w > u3P.gar_w)) ) { + fprintf(stderr, "loom: ward bogus (>%u %u %u<)\r\n", + nop_w, u3P.gar_w, sop_w); + return 0; } - if ( _ce_ward_protect() ) { - goto fail; + if ( sop_w <= (nop_w + 1) ) { + u3m_signal(c3__meme); // doesn't return + return 1; } + if ( _ce_ward_post(nop_w, sop_w) ) { + return 0; + } + + c3_assert( old_w != u3P.gar_w ); return 1; - -bail: - u3m_signal(c3__meme); -fail: - return 0; } #endif /* ifdef U3_GUARD_PAGE */ @@ -276,14 +283,20 @@ u3e_fault(void* adr_v, c3_i ser_i) c3_w bit_w = (pag_w & 31); #ifdef U3_GUARD_PAGE - // The fault happened in the guard page. - if ( gar_pag_p <= adr_p && adr_p < gar_pag_p + pag_wiz_i ) { - if ( 0 == _ce_center_guard_page() ) { + if ( pag_w == u3P.gar_w ) { + if ( !_ce_ward_clip() ) { + c3_assert(0); + return 0; + } + + if ( !(u3P.dit_w[blk_w] & (1 << bit_w)) ) { + fprintf(stderr, "loom: strange guard (%d)\r\n", pag_w); + c3_assert(0); return 0; } } else -#endif /* ifdef U3_GUARD_PAGE */ +#endif if ( 0 != (u3P.dit_w[blk_w] & (1 << bit_w)) ) { fprintf(stderr, "strange page: %d, at %p, off %x\r\n", pag_w, adr_w, adr_p); c3_assert(0); @@ -591,8 +604,8 @@ _ce_patch_compose(void) nor_w = (nwr_w + (pag_wiz_i - 1)) >> u3a_page; sou_w = (swu_w + (pag_wiz_i - 1)) >> u3a_page; - c3_assert( ((gar_pag_p >> u3a_page) >= nor_w) - && ((gar_pag_p >> u3a_page) <= (u3P.pag_w - (sou_w + 1))) ); + c3_assert( (u3P.gar_w >= nor_w) + && (u3P.gar_w <= (u3P.pag_w - (sou_w + 1))) ); } #ifdef U3_SNAPSHOT_VALIDATION @@ -867,7 +880,7 @@ _ce_loom_protect_north(c3_w pgs_w, c3_w old_w) // // NB: < pgs_w is precluded by assertion in _ce_patch_compose() // - if ( (gar_pag_p >> u3a_page) < old_w ) { + if ( u3P.gar_w < old_w ) { fprintf(stderr, "loom: guard on reprotect\r\n"); c3_assert( !_ce_ward_protect() ); } @@ -914,7 +927,7 @@ _ce_loom_protect_south(c3_w pgs_w, c3_w old_w) // // NB: >= pgs_w is precluded by assertion in _ce_patch_compose() // - if ( (gar_pag_p >> u3a_page) >= off_w ) { + if ( u3P.gar_w >= off_w ) { fprintf(stderr, "loom: guard on reprotect\r\n"); c3_assert( !_ce_ward_protect() ); } @@ -973,7 +986,7 @@ _ce_loom_mapf_north(c3_i fid_i, c3_w pgs_w, c3_w old_w) // // NB: < pgs_w is precluded by assertion in _ce_patch_compose() // - if ( (gar_pag_p >> u3a_page) < old_w ) { + if ( u3P.gar_w < old_w ) { fprintf(stderr, "loom: guard on remap\r\n"); c3_assert( !_ce_ward_protect() ); } @@ -1361,7 +1374,7 @@ u3e_live(c3_c* dir_c) } #ifdef U3_GUARD_PAGE - _ce_center_guard_page(); + c3_assert( !_ce_ward_post(nor_w, u3P.pag_w - sou_w) ); #endif } } @@ -1405,7 +1418,7 @@ u3e_init(void) u3P.pag_w = u3C.wor_i >> u3a_page; #ifdef U3_GUARD_PAGE - _ce_center_guard_page(); + c3_assert( !_ce_ward_post(0, u3P.pag_w) ); #endif _ce_loom_track_north(0, u3P.pag_w); @@ -1417,19 +1430,14 @@ void u3e_ward(u3_post low_p, u3_post hig_p) { #ifdef U3_GUARD_PAGE - const u3p(c3_w) gar_p = gar_pag_p; + c3_w nop_w = (low_p - 1) >> u3a_page; + c3_w sop_w = hig_p >> u3a_page; + c3_w pag_w = u3P.gar_w; - if ( (low_p > gar_p) || (hig_p < gar_p) ) { - _ce_center_guard_page(); - c3_assert( !_ce_flaw_protect(gar_p >> u3a_page) ); - - { - c3_w pag_w = gar_p >> u3a_page; - c3_w blk_w = (pag_w >> 5); - c3_w bit_w = (pag_w & 31); - - u3P.dit_w[blk_w] |= (1 << bit_w); - } + if ( !((pag_w > nop_w) && (pag_w < hig_p)) ) { + c3_assert( !_ce_ward_post(nop_w, sop_w) ); + c3_assert( !_ce_flaw_protect(pag_w) ); + c3_assert( u3P.dit_w[pag_w >> 5] & (1 << (pag_w & 31)) ); } #endif } From c3821c33252f52b4389b9f623083b29fd4cb7db8 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Mon, 12 Dec 2022 23:04:03 -0500 Subject: [PATCH 092/136] u3: adds facade for u3e_fault(), refactors error handling --- pkg/urbit/include/noun/events.h | 14 ++++-- pkg/urbit/noun/events.c | 88 +++++++++------------------------ pkg/urbit/noun/manage.c | 60 +++++++++++++++++++++- 3 files changed, 94 insertions(+), 68 deletions(-) diff --git a/pkg/urbit/include/noun/events.h b/pkg/urbit/include/noun/events.h index 08711dda5..5d59140cd 100644 --- a/pkg/urbit/include/noun/events.h +++ b/pkg/urbit/include/noun/events.h @@ -47,6 +47,14 @@ u3e_image sou_u; // south segment } u3e_pool; + /* u3e_flaw: loom fault result. + */ + typedef enum { + u3e_flaw_sham = 0, // bogus state + u3e_flaw_base = 1, // vm fail (mprotect) + u3e_flaw_meme = 2, // bail:meme + u3e_flaw_good = 3 // handled + } u3e_flaw; /** Globals. **/ @@ -61,10 +69,10 @@ /** Functions. **/ - /* u3e_fault(): handle a memory event with libsigsegv protocol. + /* u3e_fault(): handle a memory fault. */ - c3_i - u3e_fault(void* adr_v, c3_i ser_i); + u3e_flaw + u3e_fault(u3_post low_p, u3_post hig_p, u3_post off_p); /* u3e_save(): update the checkpoint. */ diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index aaaf4e278..0fb067881 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -210,107 +210,67 @@ _ce_ward_post(c3_w nop_w, c3_w sop_w) /* _ce_ward_clip(): hit the guard page. */ -static inline c3_i -_ce_ward_clip(void) +static inline u3e_flaw +_ce_ward_clip(c3_w nop_w, c3_w sop_w) { - const c3_w old_w = u3P.gar_w; - c3_w nop_w, sop_w; - { - u3_post low_p, hig_p; - - if ( !u3R ) { - low_p = 1; - hig_p = u3C.wor_i - 1; - } - else if ( c3y == u3a_is_north(u3R) ) { - low_p = u3R->hat_p; - hig_p = u3R->cap_p; - } - else { - low_p = u3R->cap_p; - hig_p = u3R->hat_p; - } - - nop_w = (low_p - 1) >> u3a_page; - sop_w = hig_p >> u3a_page; - } + c3_w old_w = u3P.gar_w; if ( !u3P.gar_w || ((nop_w < u3P.gar_w) && (sop_w > u3P.gar_w)) ) { fprintf(stderr, "loom: ward bogus (>%u %u %u<)\r\n", nop_w, u3P.gar_w, sop_w); - return 0; + return u3e_flaw_sham; } if ( sop_w <= (nop_w + 1) ) { - u3m_signal(c3__meme); // doesn't return - return 1; + return u3e_flaw_meme; } if ( _ce_ward_post(nop_w, sop_w) ) { - return 0; + return u3e_flaw_base; } c3_assert( old_w != u3P.gar_w ); - return 1; + + return u3e_flaw_good; } #endif /* ifdef U3_GUARD_PAGE */ -/* u3e_fault(): handle a memory event with libsigsegv protocol. +/* u3e_fault(): handle a memory fault. */ -c3_i -u3e_fault(void* adr_v, c3_i ser_i) +u3e_flaw +u3e_fault(u3_post low_p, u3_post hig_p, u3_post off_p) { - // Let the stack overflow handler run. - if ( 0 == ser_i ) { - return 0; - } - - // XX u3l_log avoid here, as it can - // cause problems when handling errors - - c3_w* adr_w = (c3_w*) adr_v; - - if ( (adr_w < u3_Loom) || (adr_w >= (u3_Loom + u3C.wor_i)) ) { - fprintf(stderr, "address %p out of loom!\r\n", adr_w); - fprintf(stderr, "loom: [%p : %p)\r\n", u3_Loom, u3_Loom + u3C.wor_i); - c3_assert(0); - return 0; - } - - u3p(c3_w) adr_p = u3a_outa(adr_w); - c3_w pag_w = adr_p >> u3a_page; - c3_w blk_w = (pag_w >> 5); - c3_w bit_w = (pag_w & 31); + c3_w pag_w = off_p >> u3a_page; + c3_w blk_w = pag_w >> 5; + c3_w bit_w = pag_w & 31; #ifdef U3_GUARD_PAGE if ( pag_w == u3P.gar_w ) { - if ( !_ce_ward_clip() ) { - c3_assert(0); - return 0; + u3e_flaw fal_e = _ce_ward_clip(low_p >> u3a_page, hig_p >> u3a_page); + + if ( u3e_flaw_good != fal_e ) { + return fal_e; } if ( !(u3P.dit_w[blk_w] & (1 << bit_w)) ) { fprintf(stderr, "loom: strange guard (%d)\r\n", pag_w); - c3_assert(0); - return 0; + return u3e_flaw_sham; } } else #endif - if ( 0 != (u3P.dit_w[blk_w] & (1 << bit_w)) ) { - fprintf(stderr, "strange page: %d, at %p, off %x\r\n", pag_w, adr_w, adr_p); - c3_assert(0); - return 0; + if ( u3P.dit_w[blk_w] & (1 << bit_w) ) { + fprintf(stderr, "loom: strange page (%d): %x\r\n", pag_w, off_p); + return u3e_flaw_sham; } u3P.dit_w[blk_w] |= (1 << bit_w); if ( _ce_flaw_protect(pag_w) ) { - c3_assert(0); - return 0; + return u3e_flaw_base; } - return 1; + return u3e_flaw_good; } /* _ce_image_open(): open or create image. diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index d2795ddf0..3a1fff7ce 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -1687,12 +1687,70 @@ _cm_limits(void) # endif } +void +_cm_water(u3_post* low_p, u3_post* hig_p) +{ + c3_w top_w, bot_w; + + if ( c3y == u3a_is_north(u3R) ) { + *low_p = u3R->hat_p - 1; + *hig_p = u3R->cap_p; + } + else { + *low_p = u3R->cap_p - 1; + *hig_p = u3R->hat_p; + } +} + /* u3m_fault(): handle a memory event with libsigsegv protocol. */ c3_i u3m_fault(void* adr_v, c3_i ser_i) { - return u3e_fault(adr_v, ser_i); + // let the stack overflow handler run. + // + if ( 0 == ser_i ) { + return 0; + } + + c3_w* adr_w = (c3_w*)adr_v; + u3_post low_p, hig_p; + + if ( (adr_w < u3_Loom) || (adr_w >= (u3_Loom + u3C.wor_i)) ) { + fprintf(stderr, "loom: external fault: %p (%p : %p)\r\n\r\n", + adr_w, u3_Loom, u3_Loom + u3C.wor_i); + c3_assert(0); + return 0; + } + + _cm_water(&low_p, &hig_p); + + switch ( u3e_fault(low_p, hig_p, u3a_outa(adr_w)) ) { + // page tracking invariants violated, fatal + // + case u3e_flaw_sham: { + c3_assert(0); + return 0; + } + + // virtual memory failure (protections), possibly recoverable XX + // + case u3e_flaw_base: { + c3_assert(0); + return 0; + } + + // loom limits exceeded, recoverable + // + case u3e_flaw_meme: { + u3m_signal(c3__meme); // doesn't return + return 1; + } + + case u3e_flaw_good: return 1; + } + + c3_assert(!"unpossible"); } /* u3m_save(): update the checkpoint. From 973cea6d91b4d660f5a20a37ed73318955a27337 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Mon, 12 Dec 2022 23:25:06 -0500 Subject: [PATCH 093/136] u3: refactors u3e_save() facade and watermark calculations --- pkg/urbit/include/noun/events.h | 2 +- pkg/urbit/include/noun/manage.h | 2 +- pkg/urbit/noun/events.c | 38 +++++++++++----------------- pkg/urbit/noun/manage.c | 44 ++++++++++++++++----------------- 4 files changed, 39 insertions(+), 47 deletions(-) diff --git a/pkg/urbit/include/noun/events.h b/pkg/urbit/include/noun/events.h index 5d59140cd..a8860f53d 100644 --- a/pkg/urbit/include/noun/events.h +++ b/pkg/urbit/include/noun/events.h @@ -77,7 +77,7 @@ /* u3e_save(): update the checkpoint. */ void - u3e_save(void); + u3e_save(u3_post low_p, u3_post hig_p); /* u3e_live(): start the persistence system. Return c3y if no image. */ diff --git a/pkg/urbit/include/noun/manage.h b/pkg/urbit/include/noun/manage.h index 0c2348573..1b50c579b 100644 --- a/pkg/urbit/include/noun/manage.h +++ b/pkg/urbit/include/noun/manage.h @@ -127,7 +127,7 @@ /* u3m_water(): produce high and low watermarks. Asserts u3R == u3H. */ void - u3m_water(c3_w *low_w, c3_w *hig_w); + u3m_water(u3_post* low_p, u3_post* hig_p); /* u3m_pretty(): dumb prettyprint to string. RETAIN. */ diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 0fb067881..9ca9298ba 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -548,25 +548,9 @@ _ce_patch_save_page(u3_ce_patch* pat_u, /* _ce_patch_compose(): make and write current patch. */ static u3_ce_patch* -_ce_patch_compose(void) +_ce_patch_compose(c3_w nor_w, c3_w sou_w) { c3_w pgs_w = 0; - c3_w nor_w = 0; - c3_w sou_w = 0; - - /* Calculate number of saved pages, north and south. - */ - { - c3_w nwr_w, swu_w; - - u3m_water(&nwr_w, &swu_w); - - nor_w = (nwr_w + (pag_wiz_i - 1)) >> u3a_page; - sou_w = (swu_w + (pag_wiz_i - 1)) >> u3a_page; - - c3_assert( (u3P.gar_w >= nor_w) - && (u3P.gar_w <= (u3P.pag_w - (sou_w + 1))) ); - } #ifdef U3_SNAPSHOT_VALIDATION u3K.nor_w = nor_w; @@ -838,7 +822,7 @@ _ce_loom_protect_north(c3_w pgs_w, c3_w old_w) #ifdef U3_GUARD_PAGE // protect guard page if clobbered // - // NB: < pgs_w is precluded by assertion in _ce_patch_compose() + // NB: < pgs_w is precluded by assertion in u3e_save() // if ( u3P.gar_w < old_w ) { fprintf(stderr, "loom: guard on reprotect\r\n"); @@ -885,7 +869,7 @@ _ce_loom_protect_south(c3_w pgs_w, c3_w old_w) #ifdef U3_GUARD_PAGE // protect guard page if clobbered // - // NB: >= pgs_w is precluded by assertion in _ce_patch_compose() + // NB: >= pgs_w is precluded by assertion in u3e_save() // if ( u3P.gar_w >= off_w ) { fprintf(stderr, "loom: guard on reprotect\r\n"); @@ -944,7 +928,7 @@ _ce_loom_mapf_north(c3_i fid_i, c3_w pgs_w, c3_w old_w) #ifdef U3_GUARD_PAGE // protect guard page if clobbered // - // NB: < pgs_w is precluded by assertion in _ce_patch_compose() + // NB: < pgs_w is precluded by assertion in u3e_save() // if ( u3P.gar_w < old_w ) { fprintf(stderr, "loom: guard on remap\r\n"); @@ -1174,7 +1158,7 @@ _ce_backup(void) before we try to make another snapshot. */ void -u3e_save(void) +u3e_save(u3_post low_p, u3_post hig_p) { u3_ce_patch* pat_u; c3_w nod_w, sod_w; @@ -1183,8 +1167,16 @@ u3e_save(void) return; } - if ( !(pat_u = _ce_patch_compose()) ) { - return; + { + c3_w nop_w = (low_p >> u3a_page); + c3_w nor_w = (low_p + (pag_wiz_i - 1)) >> u3a_page; + c3_w sop_w = hig_p >> u3a_page; + + c3_assert( (u3P.gar_w > nop_w) && (u3P.gar_w < sop_w) ); + + if ( !(pat_u = _ce_patch_compose(nor_w, u3P.pag_w - sop_w)) ) { + return; + } } nod_w = u3P.nor_u.pgs_w; diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 3a1fff7ce..05fe5ff80 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -998,12 +998,22 @@ u3m_flog(c3_w gof_w) /* u3m_water(): produce watermarks. */ void -u3m_water(c3_w* low_w, c3_w* hig_w) +u3m_water(u3_post* low_p, u3_post* hig_p) { - c3_assert(u3R == &u3H->rod_u); - - *low_w = u3a_heap(u3R); - *hig_w = u3a_temp(u3R) + c3_wiseof(u3v_home); + // in a north road, hat points to the end of the heap + 1 word, + // while cap points to the top of the stack + // + if ( c3y == u3a_is_north(u3R) ) { + *low_p = u3R->hat_p - 1; + *hig_p = u3R->cap_p; + } + // in a south road, hat points to the end of the heap, + // while cap points to the top of the stack + 1 word + // + else { + *low_p = u3R->cap_p - 1; + *hig_p = u3R->hat_p; + } } /* u3m_soft_top(): top-level safety wrapper. @@ -1687,21 +1697,6 @@ _cm_limits(void) # endif } -void -_cm_water(u3_post* low_p, u3_post* hig_p) -{ - c3_w top_w, bot_w; - - if ( c3y == u3a_is_north(u3R) ) { - *low_p = u3R->hat_p - 1; - *hig_p = u3R->cap_p; - } - else { - *low_p = u3R->cap_p - 1; - *hig_p = u3R->hat_p; - } -} - /* u3m_fault(): handle a memory event with libsigsegv protocol. */ c3_i @@ -1723,7 +1718,7 @@ u3m_fault(void* adr_v, c3_i ser_i) return 0; } - _cm_water(&low_p, &hig_p); + u3m_water(&low_p, &hig_p); switch ( u3e_fault(low_p, hig_p, u3a_outa(adr_w)) ) { // page tracking invariants violated, fatal @@ -1758,7 +1753,12 @@ u3m_fault(void* adr_v, c3_i ser_i) void u3m_save(void) { - return u3e_save(); + u3_post low_p, hig_p; + u3m_water(&low_p, &hig_p); + + c3_assert(u3R == &u3H->rod_u); + + return u3e_save(low_p, hig_p); } /* _cm_signals(): set up interrupts, etc. From ebf0ab49afe30c9119c1c353620077071139d834 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Mon, 12 Dec 2022 23:28:33 -0500 Subject: [PATCH 094/136] u3: adds facade for u3e_ward(), refactors watermarks --- pkg/urbit/include/noun/manage.h | 5 +++++ pkg/urbit/noun/events.c | 2 +- pkg/urbit/noun/manage.c | 13 +++++++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pkg/urbit/include/noun/manage.h b/pkg/urbit/include/noun/manage.h index 1b50c579b..2d9fb1822 100644 --- a/pkg/urbit/include/noun/manage.h +++ b/pkg/urbit/include/noun/manage.h @@ -44,6 +44,11 @@ void u3m_save(void); + /* u3m_ward(): tend the guard page. + */ + void + u3m_ward(void); + /* u3m_init(): start the environment. */ void diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 9ca9298ba..021e65a79 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -1382,7 +1382,7 @@ void u3e_ward(u3_post low_p, u3_post hig_p) { #ifdef U3_GUARD_PAGE - c3_w nop_w = (low_p - 1) >> u3a_page; + c3_w nop_w = low_p >> u3a_page; c3_w sop_w = hig_p >> u3a_page; c3_w pag_w = u3P.gar_w; diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 05fe5ff80..2a48baa34 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -814,7 +814,6 @@ u3m_leap(c3_w pad_w) u3R->cap_p -= len_w; rod_u = _pave_south(u3a_into(bot_p), c3_wiseof(u3a_road), len_w); - u3e_ward(rod_u->cap_p, rod_u->hat_p); #if 0 fprintf(stderr, "leap: from north %p (cap 0x%x), to south %p\r\n", u3R, @@ -827,7 +826,6 @@ u3m_leap(c3_w pad_w) u3R->cap_p += len_w; rod_u = _pave_north(u3a_into(bot_p), c3_wiseof(u3a_road), len_w); - u3e_ward(rod_u->hat_p, rod_u->cap_p); #if 0 fprintf(stderr, "leap: from south %p (cap 0x%x), to north %p\r\n", u3R, @@ -849,6 +847,7 @@ u3m_leap(c3_w pad_w) */ { u3R = rod_u; + u3m_ward(); _pave_parts(); } #ifdef U3_MEMORY_DEBUG @@ -1761,6 +1760,16 @@ u3m_save(void) return u3e_save(low_p, hig_p); } +/* u3m_ward(): tend the guard page. +*/ +void +u3m_ward(void) +{ + u3_post low_p, hig_p; + u3m_water(&low_p, &hig_p); + return u3e_ward(low_p, hig_p); +} + /* _cm_signals(): set up interrupts, etc. */ static void From af8607fde490d5359909d0a2eec64448aea7b635 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Tue, 13 Dec 2022 00:06:42 -0500 Subject: [PATCH 095/136] u3: adds comments to road pave implementations --- pkg/urbit/noun/manage.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 2a48baa34..2852ef797 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -522,6 +522,8 @@ _pave_north(c3_w* mem_w, c3_w siz_w, c3_w len_w) // the stack starts at the end of the memory segment, // minus space for the road structure [siz_w] // + // NB: the returned road is "above" the stack + // c3_w* rut_w = mem_w; c3_w* mat_w = ((mem_w + len_w) - siz_w); c3_w* cap_w = mat_w; @@ -540,6 +542,8 @@ _pave_south(c3_w* mem_w, c3_w siz_w, c3_w len_w) // the stack starts at the base memory pointer [mem_w], // and ends after the space for the road structure [siz_w] // + // NB: the returned road is *on* the stack + // c3_w* rut_w = (mem_w + len_w); c3_w* mat_w = mem_w; c3_w* cap_w = mat_w + siz_w; From a8dadedebf267272fd5591a70a3abf9ed6dec0fa Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Tue, 13 Dec 2022 11:22:13 -0500 Subject: [PATCH 096/136] vere: bumps version --- pkg/urbit/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/urbit/version b/pkg/urbit/version index 9f76d37b7..5dfd5e068 100644 --- a/pkg/urbit/version +++ b/pkg/urbit/version @@ -1 +1 @@ -1.13 \ No newline at end of file +1.14-rc1 \ No newline at end of file From 48978bd940ae92c9ee43ede5e6dd176402284cf6 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 14 Dec 2022 12:46:36 -0600 Subject: [PATCH 097/136] group-store: allow rebuilding --- pkg/landscape/app/group-store.hoon | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 7e26a80ff..b373024d6 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -140,6 +140,7 @@ ?+ q.vase !! %migrate poke-migrate:gc %export poke-export:gc + %rebuild poke-rebuild:gc == :: ?(%group-update-0 %group-action) @@ -235,6 +236,18 @@ :: |_ bol=bowl:gall +* io ~(. agentio bol) +++ poke-rebuild + ^- (quip card _state) + =/ wex ~(tap by wex.bol) + |- + ?~ wex + `state + =/ [[=wire =ship =term] [acked=? =(pole knot)]] + i.wex + ?. ?=([%gladio ship=@ ~] pole) + $(wex t.wex) + $(wex t.wex, wait (~(put in wait) (slav %p ship.pole))) +:: ++ poke-export ^- (quip card _state) :_ state @@ -256,6 +269,8 @@ ^- card [%pass /gladio/(scot %p ship) %agent [ship %groups] %watch /init] :: + +:: ++ backoff-migrate |= =ship ^- card From 4df4a16881085b8896db1400536731e9f2de5e65 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 14 Dec 2022 13:29:17 -0600 Subject: [PATCH 098/136] group-store: add %rebuild poke --- pkg/landscape/app/group-store.hoon | 41 ++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index b373024d6..c7c429a55 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -238,15 +238,36 @@ +* io ~(. agentio bol) ++ poke-rebuild ^- (quip card _state) - =/ wex ~(tap by wex.bol) - |- - ?~ wex - `state - =/ [[=wire =ship =term] [acked=? =(pole knot)]] - i.wex - ?. ?=([%gladio ship=@ ~] pole) - $(wex t.wex) - $(wex t.wex, wait (~(put in wait) (slav %p ship.pole))) + |^ + =. wait + put-missing + =^ cards state + watch-missing + [cards state] + :: + ++ watch-missing + =/ wait ~(tap in wait) + =| cards=(list card) + |- + ?~ wait + [cards state] + ?: (~(has by wex.bol) [/gladio/(scot %p i.wait) i.wait dap.bol]) + $(wait t.wait) + =. cards + :_(cards (watch-init-migrate i.wait)) + $(wait t.wait) + :: + ++ put-missing + =/ wex ~(tap by wex.bol) + |- + ?~ wex + wait + =/ [[=wire =ship =term] [acked=? =(pole knot)]] + i.wex + ?. ?=([%gladio ship=@ ~] pole) + $(wex t.wex) + $(wex t.wex, wait (~(put in wait) (slav %p ship.pole))) + -- :: ++ poke-export ^- (quip card _state) @@ -269,8 +290,6 @@ ^- card [%pass /gladio/(scot %p ship) %agent [ship %groups] %watch /init] :: - -:: ++ backoff-migrate |= =ship ^- card From 5d4142eff88508aed736d51488cc0d856e2c83e8 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Tue, 13 Dec 2022 21:16:30 -0500 Subject: [PATCH 099/136] u3: ports minor noun/ cleanup --- pkg/urbit/noun/events.c | 1 + pkg/urbit/noun/manage.c | 4 ++++ pkg/urbit/noun/trace.c | 17 +++++++++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 021e65a79..7b1f78b21 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -432,6 +432,7 @@ _ce_patch_verify(u3_ce_patch* pat_u) } return c3n; } + { c3_w nug_w = u3r_mug_words(mem_w, pag_wiz_i); diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 2852ef797..02a881d73 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -1946,6 +1946,10 @@ void u3m_stop() { u3je_secp_stop(); + + // XX move to jets.c + // + c3_free(u3D.ray_u); } /* u3m_boot(): start the u3 system. return next event, starting from 1. diff --git a/pkg/urbit/noun/trace.c b/pkg/urbit/noun/trace.c index 79d290930..85afcfd46 100644 --- a/pkg/urbit/noun/trace.c +++ b/pkg/urbit/noun/trace.c @@ -278,11 +278,19 @@ void u3t_trace_open(c3_c* dir_c) { c3_c fil_c[2048]; + + if ( !dir_c ) { + return; + } + snprintf(fil_c, 2048, "%s/.urb/put/trace", dir_c); struct stat st; - if ( -1 == stat(fil_c, &st) ) { - c3_mkdir(fil_c, 0700); + if ( (-1 == stat(fil_c, &st)) + && (-1 == c3_mkdir(fil_c, 0700)) ) + { + fprintf(stderr, "mkdir: %s failed: %s\r\n", fil_c, strerror(errno)); + return; } c3_c lif_c[2056]; @@ -291,6 +299,11 @@ u3t_trace_open(c3_c* dir_c) u3_Host.tra_u.fil_u = c3_fopen(lif_c, "w"); u3_Host.tra_u.nid_w = (int)getpid(); + if ( !u3_Host.tra_u.fil_u) { + fprintf(stderr, "trace open: %s\r\n", strerror(errno)); + return; + } + fprintf(u3_Host.tra_u.fil_u, "[ "); // We have two "threads", the event processing and the nock stuff. From 535c49d2dbb48c82824ecb4dd35b758e8d83a014 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Tue, 13 Dec 2022 11:54:27 -0500 Subject: [PATCH 100/136] u3: factors out |mass-style memory measurement --- pkg/urbit/include/noun/allocate.h | 5 ++ pkg/urbit/noun/allocate.c | 122 ++++++++++++++++++++++++++- pkg/urbit/worker/serf.c | 133 +----------------------------- 3 files changed, 125 insertions(+), 135 deletions(-) diff --git a/pkg/urbit/include/noun/allocate.h b/pkg/urbit/include/noun/allocate.h index 964de538b..03b219c08 100644 --- a/pkg/urbit/include/noun/allocate.h +++ b/pkg/urbit/include/noun/allocate.h @@ -645,6 +645,11 @@ void u3a_print_memory(FILE* fil_u, c3_c* cap_c, c3_w wor_w); + /* u3a_prof(): mark/measure/print memory profile. RETAIN. + */ + c3_w + u3a_prof(FILE* fil_u, c3_w den_w, u3_noun mas); + /* u3a_maid(): maybe print memory. */ c3_w diff --git a/pkg/urbit/noun/allocate.c b/pkg/urbit/noun/allocate.c index 4667b7ba8..093c6fefb 100644 --- a/pkg/urbit/noun/allocate.c +++ b/pkg/urbit/noun/allocate.c @@ -1627,7 +1627,7 @@ u3a_mark_ptr(void* ptr_v) if ( 0 == box_u->eus_w ) { siz_w = box_u->siz_w; } - else if ( 0xffffffff == box_u->eus_w ) { // see _raft_prof() + else if ( 0xffffffff == box_u->eus_w ) { // see u3a_prof() siz_w = 0xffffffff; box_u->eus_w = 0; } @@ -1645,7 +1645,7 @@ u3a_mark_ptr(void* ptr_v) else { c3_assert(use_ws != 0); - if ( 0x80000000 == (c3_w)use_ws ) { // see _raft_prof() + if ( 0x80000000 == (c3_w)use_ws ) { // see u3a_prof() use_ws = -1; siz_w = 0xffffffff; } @@ -1916,7 +1916,7 @@ u3a_print_memory(FILE* fil_u, c3_c* cap_c, c3_w wor_w) if ( byt_w ) { if ( gib_w ) { fprintf(fil_u, "%s: GB/%d.%03d.%03d.%03d\r\n", - cap_c, gib_w, mib_w, kib_w, bib_w); + cap_c, gib_w, mib_w, kib_w, bib_w); } else if ( mib_w ) { fprintf(fil_u, "%s: MB/%d.%03d.%03d\r\n", cap_c, mib_w, kib_w, bib_w); @@ -1941,6 +1941,122 @@ u3a_maid(FILE* fil_u, c3_c* cap_c, c3_w wor_w) return wor_w; } +/* _ca_print_memory(): un-captioned u3a_print_memory(). +*/ +static void +_ca_print_memory(FILE* fil_u, c3_w wor_w) +{ + c3_w byt_w = (wor_w * 4); + c3_w gib_w = (byt_w / 1000000000); + c3_w mib_w = (byt_w % 1000000000) / 1000000; + c3_w kib_w = (byt_w % 1000000) / 1000; + c3_w bib_w = (byt_w % 1000); + + if ( gib_w ) { + fprintf(fil_u, "GB/%d.%03d.%03d.%03d\r\n", + gib_w, mib_w, kib_w, bib_w); + } + else if ( mib_w ) { + fprintf(fil_u, "MB/%d.%03d.%03d\r\n", mib_w, kib_w, bib_w); + } + else if ( kib_w ) { + fprintf(fil_u, "KB/%d.%03d\r\n", kib_w, bib_w); + } + else { + fprintf(fil_u, "B/%d\r\n", bib_w); + } +} + +/* u3a_prof(): mark/measure/print memory profile. RETAIN. +*/ +c3_w +u3a_prof(FILE* fil_u, c3_w den_w, u3_noun mas) +{ + c3_w tot_w = 0; + u3_noun h_mas, t_mas; + + if ( c3n == u3r_cell(mas, &h_mas, &t_mas) ) { + fprintf(fil_u, "%.*smistyped mass\r\n", den_w, ""); + return tot_w; + } + else if ( _(u3du(h_mas)) ) { + fprintf(fil_u, "%.*smistyped mass head\r\n", den_w, ""); + { + c3_c* lab_c = u3m_pretty(h_mas); + fprintf(fil_u, "h_mas: %s", lab_c); + c3_free(lab_c); + } + return tot_w; + } + else { + { + c3_c* lab_c = u3m_pretty(h_mas); + fprintf(fil_u, "%*s%s: ", den_w, "", lab_c); + c3_free(lab_c); + } + + u3_noun it_mas, tt_mas; + + if ( c3n == u3r_cell(t_mas, &it_mas, &tt_mas) ) { + fprintf(fil_u, "%*smistyped mass tail\r\n", den_w, ""); + return tot_w; + } + else if ( c3y == it_mas ) { + tot_w += u3a_mark_noun(tt_mas); + _ca_print_memory(fil_u, tot_w); + +#if 1 + /* The basic issue here is that tt_mas is included in .sac + * (the whole profile), so they can't both be roots in the + * normal sense. When we mark .sac later on, we want tt_mas + * to appear unmarked, but its children should be already + * marked. + * + * see u3a_mark_ptr(). + */ + if ( _(u3a_is_dog(tt_mas)) ) { + u3a_box* box_u = u3a_botox(u3a_to_ptr(tt_mas)); +#ifdef U3_MEMORY_DEBUG + if ( 1 == box_u->eus_w ) { + box_u->eus_w = 0xffffffff; + } + else { + box_u->eus_w -= 1; + } +#else + if ( -1 == (c3_w)box_u->use_w ) { + box_u->use_w = 0x80000000; + } + else { + box_u->use_w += 1; + } +#endif + } +#endif + + return tot_w; + } + else if ( c3n == it_mas ) { + fprintf(fil_u, "\r\n"); + + while ( _(u3du(tt_mas)) ) { + tot_w += u3a_prof(fil_u, den_w+2, u3h(tt_mas)); + tt_mas = u3t(tt_mas); + } + + fprintf(fil_u, "%*s--", den_w, ""); + _ca_print_memory(fil_u, tot_w); + + return tot_w; + + } + else { + fprintf(fil_u, "%*smistyped (strange) mass tail\r\n", den_w, ""); + return tot_w; + } + } +} + /* u3a_mark_road(): mark ad-hoc persistent road structures. */ c3_w diff --git a/pkg/urbit/worker/serf.c b/pkg/urbit/worker/serf.c index 4728895b7..75c8e8611 100644 --- a/pkg/urbit/worker/serf.c +++ b/pkg/urbit/worker/serf.c @@ -49,137 +49,6 @@ -- */ -/* _serf_space(): print n spaces. -*/ -static void -_serf_space(FILE* fil_u, c3_w n) -{ - for (; n > 0; n--) - (fprintf(fil_u," ")); -} - -/* _serf_print_memory(): print memory amount. -** -** Helper for _serf_prof(), just an un-captioned u3a_print_memory(). -*/ -static void -_serf_print_memory(FILE* fil_u, c3_w wor_w) -{ - c3_w byt_w = (wor_w * 4); - c3_w gib_w = (byt_w / 1000000000); - c3_w mib_w = (byt_w % 1000000000) / 1000000; - c3_w kib_w = (byt_w % 1000000) / 1000; - c3_w bib_w = (byt_w % 1000); - - if ( gib_w ) { - (fprintf(fil_u, "GB/%d.%03d.%03d.%03d\r\n", - gib_w, mib_w, kib_w, bib_w)); - } - else if ( mib_w ) { - (fprintf(fil_u, "MB/%d.%03d.%03d\r\n", mib_w, kib_w, bib_w)); - } - else if ( kib_w ) { - (fprintf(fil_u, "KB/%d.%03d\r\n", kib_w, bib_w)); - } - else { - (fprintf(fil_u, "B/%d\r\n", bib_w)); - } -} - -/* _serf_prof(): print memory profile. RETAIN. -*/ -c3_w -_serf_prof(FILE* fil_u, c3_w den, u3_noun mas) -{ - c3_w tot_w = 0; - u3_noun h_mas, t_mas; - - if ( c3n == u3r_cell(mas, &h_mas, &t_mas) ) { - _serf_space(fil_u, den); - fprintf(fil_u, "mistyped mass\r\n"); - return tot_w; - } - else if ( _(u3du(h_mas)) ) { - _serf_space(fil_u, den); - fprintf(fil_u, "mistyped mass head\r\n"); - { - c3_c* lab_c = u3m_pretty(h_mas); - fprintf(fil_u, "h_mas: %s", lab_c); - c3_free(lab_c); - } - return tot_w; - } - else { - _serf_space(fil_u, den); - - { - c3_c* lab_c = u3m_pretty(h_mas); - fprintf(fil_u, "%s: ", lab_c); - c3_free(lab_c); - } - - u3_noun it_mas, tt_mas; - - if ( c3n == u3r_cell(t_mas, &it_mas, &tt_mas) ) { - fprintf(fil_u, "mistyped mass tail\r\n"); - return tot_w; - } - else if ( c3y == it_mas ) { - tot_w += u3a_mark_noun(tt_mas); - _serf_print_memory(fil_u, tot_w); - -#if 1 - /* The basic issue here is that tt_mas is included in .sac - * (the whole profile), so they can't both be roots in the - * normal sense. When we mark .sac later on, we want tt_mas - * to appear unmarked, but its children should be already - * marked. - */ - if ( _(u3a_is_dog(tt_mas)) ) { - u3a_box* box_u = u3a_botox(u3a_to_ptr(tt_mas)); -#ifdef U3_MEMORY_DEBUG - if ( 1 == box_u->eus_w ) { - box_u->eus_w = 0xffffffff; - } - else { - box_u->eus_w -= 1; - } -#else - if ( -1 == (c3_w)box_u->use_w ) { - box_u->use_w = 0x80000000; - } - else { - box_u->use_w += 1; - } -#endif - } -#endif - - return tot_w; - } - else if ( c3n == it_mas ) { - fprintf(fil_u, "\r\n"); - - while ( _(u3du(tt_mas)) ) { - tot_w += _serf_prof(fil_u, den+2, u3h(tt_mas)); - tt_mas = u3t(tt_mas); - } - - _serf_space(fil_u, den); - fprintf(fil_u, "--"); - _serf_print_memory(fil_u, tot_w); - - return tot_w; - - } - else { - _serf_space(fil_u, den); - fprintf(fil_u, "mistyped (strange) mass tail\r\n"); - return tot_w; - } - } -} - /* _serf_grab(): garbage collect, checking for profiling. RETAIN. */ static void @@ -225,7 +94,7 @@ _serf_grab(u3_noun sac) c3_assert( u3R == &(u3H->rod_u) ); fprintf(fil_u, "\r\n"); - tot_w += u3a_maid(fil_u, "total userspace", _serf_prof(fil_u, 0, sac)); + tot_w += u3a_maid(fil_u, "total userspace", u3a_prof(fil_u, 0, sac)); tot_w += u3m_mark(fil_u); tot_w += u3a_maid(fil_u, "space profile", u3a_mark_noun(sac)); From fdafdf1f125b8bcd8e523bf587bf4bf53efd4b2c Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Tue, 13 Dec 2022 12:23:47 -0500 Subject: [PATCH 101/136] u3: ports new arvo invocations for boot and +poke --- pkg/urbit/include/noun/vortex.h | 5 +++ pkg/urbit/noun/vortex.c | 69 ++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/pkg/urbit/include/noun/vortex.h b/pkg/urbit/include/noun/vortex.h index 92320620a..ed24e691e 100644 --- a/pkg/urbit/include/noun/vortex.h +++ b/pkg/urbit/include/noun/vortex.h @@ -87,6 +87,11 @@ u3_noun u3v_poke(u3_noun ovo); + /* u3v_poke_sure(): inject an event, saving new state if successful. + */ + c3_o + u3v_poke_sure(c3_w mil_w, u3_noun eve, u3_noun* pro); + /* u3v_tank(): dump single tank. */ void diff --git a/pkg/urbit/noun/vortex.c b/pkg/urbit/noun/vortex.c index 016588ea8..a37b76888 100644 --- a/pkg/urbit/noun/vortex.c +++ b/pkg/urbit/noun/vortex.c @@ -27,9 +27,18 @@ u3v_life(u3_noun eve) c3_o u3v_boot(u3_noun eve) { + c3_d len_d; + + { + u3_noun len = u3qb_lent(eve); + c3_assert( c3y == u3r_safe_chub(len, &len_d) ); + u3z(len); + } + // ensure zero-initialized kernel // - u3A->roc = 0; + u3A->roc = 0; + u3A->eve_d = 0; { u3_noun pro = u3m_soft(0, u3v_life, eve); @@ -39,7 +48,8 @@ u3v_boot(u3_noun eve) return c3n; } - u3A->roc = u3k(u3t(pro)); + u3A->roc = u3k(u3t(pro)); + u3A->eve_d = len_d; u3z(pro); } @@ -260,6 +270,61 @@ u3v_poke(u3_noun ovo) return pro; } +/* _cv_poke_eve(): u3v_poke w/out u3A->now XX replace +*/ +static u3_noun +_cv_poke_eve(u3_noun sam) +{ + u3_noun fun = u3n_nock_on(u3k(u3A->roc), u3k(u3x_at(_CVX_POKE, u3A->roc))); + u3_noun pro; + + { +# ifdef U3_MEMORY_DEBUG + c3_w cod_w = u3a_lush(u3h(u3t(u3t(sam)))); +# endif + + pro = u3n_slam_on(fun, sam); + +# ifdef U3_MEMORY_DEBUG + u3a_lop(cod_w); +# endif + } + + return pro; +} + +/* u3v_poke_sure(): inject an event, saving new state if successful. +*/ +c3_o +u3v_poke_sure(c3_w mil_w, u3_noun eve, u3_noun* pro) +{ + u3_noun gon = u3m_soft(mil_w, _cv_poke_eve, eve); + u3_noun tag, dat; + u3x_cell(gon, &tag, &dat); + + // event failed, produce trace + // + if ( u3_blip != tag ) { + *pro = gon; + return c3n; + } + + // event succeeded, persist state and produce effects + // + { + u3_noun vir, cor; + u3x_cell(dat, &vir, &cor); + + u3z(u3A->roc); + u3A->roc = u3k(cor); + u3A->eve_d++; + + *pro = u3k(vir); + u3z(gon); + return c3y; + } +} + /* u3v_tank(): dump single tank. */ void From 5a775d1c79ad6609f27f2540e8966d55728497fd Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Tue, 13 Dec 2022 18:28:41 -0500 Subject: [PATCH 102/136] u3: ports meld measurement printfs --- pkg/urbit/daemon/main.c | 4 +--- pkg/urbit/include/noun/urth.h | 2 +- pkg/urbit/noun/urth.c | 8 ++++++-- pkg/urbit/worker/serf.c | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index 183a1ae98..5d7f5c920 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -1619,9 +1619,7 @@ _cw_meld(c3_i argc, c3_c* argv[]) u3C.wag_w |= u3o_hashless; u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); - pre_w = u3a_open(u3R); - u3u_meld(); - u3a_print_memory(stderr, "urbit: meld: gained", (u3a_open(u3R) - pre_w)); + u3a_print_memory(stderr, "urbit: meld: gained", u3u_meld()); u3m_save(); u3_disk_exit(log_u); diff --git a/pkg/urbit/include/noun/urth.h b/pkg/urbit/include/noun/urth.h index 3fe29792e..2c6617f4b 100644 --- a/pkg/urbit/include/noun/urth.h +++ b/pkg/urbit/include/noun/urth.h @@ -5,7 +5,7 @@ **/ /* u3u_meld(): globally deduplicate memory. */ - void + c3_w u3u_meld(void); /* u3u_cram(): globably deduplicate memory, and write a rock to disk. diff --git a/pkg/urbit/noun/urth.c b/pkg/urbit/noun/urth.c index 22163e9dd..de4e2d560 100644 --- a/pkg/urbit/noun/urth.c +++ b/pkg/urbit/noun/urth.c @@ -426,15 +426,17 @@ _cu_realloc(FILE* fil_u, ur_root_t** tor_u, ur_nvec_t* doc_u) /* u3u_meld(): globally deduplicate memory. */ #ifdef U3_MEMORY_DEBUG -void +c3_w u3u_meld(void) { fprintf(stderr, "u3: unable to meld under U3_MEMORY_DEBUG\r\n"); + return 0; } #else -void +c3_w u3u_meld(void) { + c3_w pre_w = u3a_open(u3R); ur_root_t* rot_u; ur_nvec_t cod_u; @@ -446,6 +448,8 @@ u3u_meld(void) // ur_nvec_free(&cod_u); ur_root_free(rot_u); + + return (u3a_open(u3R) - pre_w); } #endif diff --git a/pkg/urbit/worker/serf.c b/pkg/urbit/worker/serf.c index 75c8e8611..377d26fc0 100644 --- a/pkg/urbit/worker/serf.c +++ b/pkg/urbit/worker/serf.c @@ -850,7 +850,7 @@ u3_serf_live(u3_serf* sef_u, u3_noun com, u3_noun* ret) } else { u3z(com); - u3u_meld(); + u3a_print_memory(stderr, "serf: meld: gained", u3u_meld()); *ret = u3nc(c3__live, u3_nul); return c3y; } From 91f7818ab78dc6b07b2cd8930d041101d8ddc2e9 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Tue, 13 Dec 2022 21:32:03 -0500 Subject: [PATCH 103/136] vere: factors out sift/etch functions event serialization --- pkg/urbit/include/vere/vere.h | 17 ++++++ pkg/urbit/vere/disk.c | 107 ++++++++++++++++++++++------------ 2 files changed, 87 insertions(+), 37 deletions(-) diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index dd505d085..fd458a8b8 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -943,6 +943,23 @@ u3_disk* u3_disk_init(c3_c* pax_c, u3_disk_cb cb_u); + /* u3_disk_etch(): serialize an event for persistence. RETAIN [eve] + */ + size_t + u3_disk_etch(u3_disk* log_u, + u3_noun eve, + c3_l mug_l, + c3_y** out_y); + + /* u3_disk_sift(): parse a persisted event buffer. + */ + c3_o + u3_disk_sift(u3_disk* log_u, + size_t len_i, + c3_y* dat_y, + c3_l* mug_l, + u3_noun* job); + /* u3_disk_info(): status info as $mass. */ u3_noun diff --git a/pkg/urbit/vere/disk.c b/pkg/urbit/vere/disk.c index eb4a27533..2de1cb553 100644 --- a/pkg/urbit/vere/disk.c +++ b/pkg/urbit/vere/disk.c @@ -149,34 +149,47 @@ _disk_commit_start(struct _cd_save* req_u) _disk_commit_after_cb); } -/* _disk_serialize_v1(): serialize events in format v1. + +/* u3_disk_etch(): serialize an event for persistence. RETAIN [eve] */ -static c3_w -_disk_serialize_v1(u3_fact* tac_u, c3_y** out_y) +size_t +u3_disk_etch(u3_disk* log_u, + u3_noun eve, + c3_l mug_l, + c3_y** out_y) { + size_t len_i; + c3_y* dat_y; + #ifdef DISK_TRACE_JAM - u3t_event_trace("king disk jam", 'B'); + u3t_event_trace("disk etch", 'B'); #endif + // XX check version number in log_u + // XX needs api redesign to limit allocations + // { - u3_atom mat = u3qe_jam(tac_u->job); + u3_atom mat = u3qe_jam(eve); c3_w len_w = u3r_met(3, mat); - c3_y* dat_y = c3_malloc(4 + len_w); - dat_y[0] = tac_u->mug_l & 0xff; - dat_y[1] = (tac_u->mug_l >> 8) & 0xff; - dat_y[2] = (tac_u->mug_l >> 16) & 0xff; - dat_y[3] = (tac_u->mug_l >> 24) & 0xff; + + len_i = 4 + len_w; + dat_y = c3_malloc(len_i); + + dat_y[0] = mug_l & 0xff; + dat_y[1] = (mug_l >> 8) & 0xff; + dat_y[2] = (mug_l >> 16) & 0xff; + dat_y[3] = (mug_l >> 24) & 0xff; u3r_bytes(0, len_w, dat_y + 4, mat); + u3z(mat); + } + #ifdef DISK_TRACE_JAM - u3t_event_trace("king disk jam", 'E'); + u3t_event_trace("disk etch", 'E'); #endif - u3z(mat); - - *out_y = dat_y; - return len_w + 4; - } + *out_y = dat_y; + return len_i; } /* _disk_batch(): create a write batch @@ -200,7 +213,8 @@ _disk_batch(u3_disk* log_u, c3_d len_d) for ( c3_d i_d = 0ULL; i_d < len_d; ++i_d) { c3_assert( (req_u->eve_d + i_d) == tac_u->eve_d ); - req_u->siz_i[i_d] = _disk_serialize_v1(tac_u, &req_u->byt_y[i_d]); + req_u->siz_i[i_d] = u3_disk_etch(log_u, tac_u->job, + tac_u->mug_l, &req_u->byt_y[i_d]); tac_u = tac_u->nex_u; } @@ -358,6 +372,41 @@ _disk_read_done_cb(uv_timer_t* tim_u) _disk_read_close(red_u); } +/* u3_disk_sift(): parse a persisted event buffer. +*/ +c3_o +u3_disk_sift(u3_disk* log_u, + size_t len_i, + c3_y* dat_y, + c3_l* mug_l, + u3_noun* job) +{ + if ( 4 >= len_i ) { + return c3n; + } + +#ifdef DISK_TRACE_CUE + u3t_event_trace("disk sift", 'B'); +#endif + + // XX check version in log_u + // + *mug_l = dat_y[0] + ^ (dat_y[1] << 8) + ^ (dat_y[2] << 16) + ^ (dat_y[3] << 24); + + // XX u3m_soft? + // + *job = u3ke_cue(u3i_bytes(len_i - 4, dat_y + 4)); + +#ifdef DISK_TRACE_CUE + u3t_event_trace("disk sift", 'E'); +#endif + + return c3y; +} + /* _disk_read_one_cb(): lmdb read callback, invoked for each event in order */ static c3_o @@ -367,29 +416,13 @@ _disk_read_one_cb(void* ptr_v, c3_d eve_d, size_t val_i, void* val_p) u3_disk* log_u = red_u->log_u; u3_fact* tac_u; - if ( 4 >= val_i ) { - return c3n; - } - { u3_noun job; - c3_y* dat_y = val_p; - c3_l mug_l = dat_y[0] - ^ (dat_y[1] << 8) - ^ (dat_y[2] << 16) - ^ (dat_y[3] << 24); + c3_l mug_l; -#ifdef DISK_TRACE_CUE - u3t_event_trace("king disk cue", 'B'); -#endif - - // XX u3m_soft? - // - job = u3ke_cue(u3i_bytes(val_i - 4, dat_y + 4)); - -#ifdef DISK_TRACE_CUE - u3t_event_trace("king disk cue", 'E'); -#endif + if ( c3n == u3_disk_sift(log_u, val_i, (c3_y*)val_p, &mug_l, &job) ) { + return c3n; + } tac_u = u3_fact_init(eve_d, mug_l, job); } From 99a8ccda7be39827d4936623520c1a0ca05c8523 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Tue, 13 Dec 2022 11:50:56 -0500 Subject: [PATCH 104/136] vere: ports synchronous lmdb iterator --- pkg/urbit/include/vere/db/lmdb.h | 29 +++++++++ pkg/urbit/vere/db/lmdb.c | 106 +++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/pkg/urbit/include/vere/db/lmdb.h b/pkg/urbit/include/vere/db/lmdb.h index 573a68fa4..a3ca6a414 100644 --- a/pkg/urbit/include/vere/db/lmdb.h +++ b/pkg/urbit/include/vere/db/lmdb.h @@ -6,6 +6,17 @@ /* lmdb api wrapper */ + /* u3_lmdb_iter: event iterator + */ + typedef struct _u3_lmdb_walk { + MDB_txn* txn_u; // transaction handle + MDB_dbi mdb_u; // db handle + MDB_cursor* cur_u; // db cursor + c3_o red_o; // have we read from this yet? + c3_d nex_d; // next event number + c3_d las_d; // final event number, inclusive + } u3_lmdb_walk; + /* u3_lmdb_init(): open lmdb at [pax_c], mmap up to [siz_i]. */ MDB_env* @@ -61,4 +72,22 @@ size_t val_i, void* val_p); + /* u3_lmdb_walk_init(): initialize db iterator. + */ + c3_o + u3_lmdb_walk_init(MDB_env* env_u, + u3_lmdb_walk* itr_u, + c3_d nex_d, + c3_d las_d); + + /* u3_lmdb_walk_next(): synchronously read next event from iterator. + */ + c3_o + u3_lmdb_walk_next(u3_lmdb_walk* itr_u, size_t* len_i, void** buf_v); + + /* u3_lmdb_walk_done(): close iterator. + */ + void + u3_lmdb_walk_done(u3_lmdb_walk* itr_u); + #endif /* ifndef U3_VERE_DB_LMDB_H */ diff --git a/pkg/urbit/vere/db/lmdb.c b/pkg/urbit/vere/db/lmdb.c index 443f11525..c95cf38c1 100644 --- a/pkg/urbit/vere/db/lmdb.c +++ b/pkg/urbit/vere/db/lmdb.c @@ -225,6 +225,112 @@ u3_lmdb_gulf(MDB_env* env_u, c3_d* low_d, c3_d* hig_d) } } +/* u3_lmdb_walk_init(): initialize db iterator. +*/ +c3_o +u3_lmdb_walk_init(MDB_env* env_u, + u3_lmdb_walk* itr_u, + c3_d nex_d, + c3_d las_d) +{ + // XX assumes little-endian + // + MDB_val key_u = { .mv_size = sizeof(c3_d), .mv_data = &nex_d }; + MDB_val val_u; + c3_w ops_w, ret_w; + + itr_u->red_o = c3n; + itr_u->nex_d = nex_d; + itr_u->las_d = las_d; + + // create a read-only transaction. + // + ops_w = MDB_RDONLY; + if ( (ret_w = mdb_txn_begin(env_u, 0, ops_w, &itr_u->txn_u)) ) { + mdb_logerror(stderr, ret_w, "lmdb: read txn_begin fail"); + return c3n; + } + // open the database in the transaction + // + ops_w = MDB_CREATE | MDB_INTEGERKEY; + if ( (ret_w = mdb_dbi_open(itr_u->txn_u, "EVENTS", ops_w, &itr_u->mdb_u)) ) { + mdb_logerror(stderr, ret_w, "lmdb: read: dbi_open fail"); + // XX confirm + // + mdb_txn_abort(itr_u->txn_u); + return c3n; + } + + // creates a cursor to iterate over keys starting at [eve_d] + // + if ( (ret_w = mdb_cursor_open(itr_u->txn_u, itr_u->mdb_u, &itr_u->cur_u)) ) { + mdb_logerror(stderr, ret_w, "lmdb: read: cursor_open fail"); + // XX confirm + // + mdb_txn_abort(itr_u->txn_u); + return c3n; + } + + // set the cursor to the position of [eve_d] + // + ops_w = MDB_SET_KEY; + if ( (ret_w = mdb_cursor_get(itr_u->cur_u, &key_u, &val_u, ops_w)) ) { + mdb_logerror(stderr, ret_w, "lmdb: read: initial cursor_get failed"); + fprintf(stderr, " at %" PRIu64 "\r\n", nex_d); + mdb_cursor_close(itr_u->cur_u); + // XX confirm + // + mdb_txn_abort(itr_u->txn_u); + return c3n; + } + + return c3y; +} + +/* u3_lmdb_walk_next(): synchronously read next event from iterator. +*/ +c3_o +u3_lmdb_walk_next(u3_lmdb_walk* itr_u, size_t* len_i, void** buf_v) +{ + MDB_val key_u, val_u; + c3_w ret_w, ops_w; + + c3_assert( itr_u->nex_d <= itr_u->las_d ); + + ops_w = ( c3y == itr_u->red_o ) ? MDB_NEXT : MDB_GET_CURRENT; + if ( (ret_w = mdb_cursor_get(itr_u->cur_u, &key_u, &val_u, ops_w)) ) { + mdb_logerror(stderr, ret_w, "lmdb: walk error"); + return c3n; + } + + // sanity check: ensure contiguous event numbers + // + if ( *(c3_d*)key_u.mv_data != itr_u->nex_d ) { + fprintf(stderr, "lmdb: read gap: expected %" PRIu64 + ", received %" PRIu64 "\r\n", + itr_u->nex_d, + *(c3_d*)key_u.mv_data); + return c3n; + } + + *len_i = val_u.mv_size; + *buf_v = val_u.mv_data; + + itr_u->nex_d++; + itr_u->red_o = c3y; + + return c3y; +} + +/* u3_lmdb_walk_done(): close iterator. +*/ +void +u3_lmdb_walk_done(u3_lmdb_walk* itr_u) +{ + mdb_cursor_close(itr_u->cur_u); + mdb_txn_abort(itr_u->txn_u); +} + /* u3_lmdb_read(): read [len_d] events starting at [eve_d]. */ c3_o From 9daab2fd5a6cf0fdb06515ec8e276ef1daeaa830 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Tue, 13 Dec 2022 21:33:05 -0500 Subject: [PATCH 105/136] vere: ports synchronous event log iterator --- pkg/urbit/include/vere/vere.h | 26 ++++++++++++ pkg/urbit/vere/disk.c | 74 +++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index fd458a8b8..4b5682f24 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -563,6 +563,10 @@ u3_info put_u; // write queue } u3_disk; + /* u3_disk_walk: opaque event log iterator. + */ + typedef struct _u3_disk_walk u3_disk_walk; + /* u3_psat: pier state. */ typedef enum { @@ -1011,6 +1015,28 @@ void u3_disk_plan(u3_disk* log_u, u3_fact* tac_u); + /* u3_disk_walk_init(): init iterator. + */ + u3_disk_walk* + u3_disk_walk_init(u3_disk* log_u, + c3_d eve_d, + c3_d len_d); + + /* u3_disk_walk_live(): check if live. + */ + c3_o + u3_disk_walk_live(u3_disk_walk* wok_u); + + /* u3_disk_walk_live(): get next fact. + */ + c3_o + u3_disk_walk_step(u3_disk_walk* wok_u, u3_fact* tac_u); + + /* u3_disk_walk_done(): close iterator. + */ + void + u3_disk_walk_done(u3_disk_walk* wok_u); + /* u3_lord_init(): start serf. */ u3_lord* diff --git a/pkg/urbit/vere/disk.c b/pkg/urbit/vere/disk.c index 2de1cb553..66a8df398 100644 --- a/pkg/urbit/vere/disk.c +++ b/pkg/urbit/vere/disk.c @@ -22,6 +22,12 @@ struct _cd_save { struct _u3_disk* log_u; }; +struct _u3_disk_walk { + u3_lmdb_walk itr_u; + u3_disk* log_u; + c3_o liv_o; +}; + #undef VERBOSE_DISK #undef DISK_TRACE_JAM #undef DISK_TRACE_CUE @@ -494,6 +500,74 @@ u3_disk_read(u3_disk* log_u, c3_d eve_d, c3_d len_d) uv_timer_start(&red_u->tim_u, _disk_read_start_cb, 0, 0); } +/* u3_disk_walk_init(): init iterator. +*/ +u3_disk_walk* +u3_disk_walk_init(u3_disk* log_u, + c3_d eve_d, + c3_d len_d) +{ + u3_disk_walk* wok_u = c3_malloc(sizeof(*wok_u)); + c3_d max_d = eve_d + len_d - 1; + + wok_u->log_u = log_u; + wok_u->liv_o = u3_lmdb_walk_init(log_u->mdb_u, + &wok_u->itr_u, + eve_d, + c3_min(max_d, log_u->dun_d)); + + return wok_u; +} + +/* u3_disk_walk_live(): check if live. +*/ +c3_o +u3_disk_walk_live(u3_disk_walk* wok_u) +{ + if ( wok_u->itr_u.nex_d > wok_u->itr_u.las_d ) { + wok_u->liv_o = c3n; + } + + return wok_u->liv_o; +} + +/* u3_disk_walk_step(): get next fact. +*/ +c3_o +u3_disk_walk_step(u3_disk_walk* wok_u, u3_fact* tac_u) +{ + u3_disk* log_u = wok_u->log_u; + size_t len_i; + void* buf_v; + + tac_u->eve_d = wok_u->itr_u.nex_d; + + if ( c3n == u3_lmdb_walk_next(&wok_u->itr_u, &len_i, &buf_v) ) { + fprintf(stderr, "disk: (%" PRIu64 "): read fail\r\n", tac_u->eve_d); + return wok_u->liv_o = c3n; + } + + if ( c3n == u3_disk_sift(log_u, len_i, + (c3_y*)buf_v, + &tac_u->mug_l, + &tac_u->job) ) + { + fprintf(stderr, "disk: (%" PRIu64 "): sift fail\r\n", tac_u->eve_d); + return wok_u->liv_o = c3n; + } + + return c3y; +} + +/* u3_disk_walk_done(): close iterator. +*/ +void +u3_disk_walk_done(u3_disk_walk* wok_u) +{ + u3_lmdb_walk_done(&wok_u->itr_u); + c3_free(wok_u); +} + /* _disk_save_meta(): serialize atom, save as metadata at [key_c]. */ static c3_o From a5362f2af0241ab0776e0952a500f14a27762415 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 14 Dec 2022 01:48:20 -0500 Subject: [PATCH 106/136] vere: ports u3_disk_read_list() --- pkg/urbit/include/vere/vere.h | 5 ++++ pkg/urbit/vere/disk.c | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index 4b5682f24..ed718fd79 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -1015,6 +1015,11 @@ void u3_disk_plan(u3_disk* log_u, u3_fact* tac_u); + /* u3_disk_read_list(): synchronously read a cons list of events. + */ + u3_weak + u3_disk_read_list(u3_disk* log_u, c3_d eve_d, c3_d len_d, c3_l* mug_l); + /* u3_disk_walk_init(): init iterator. */ u3_disk_walk* diff --git a/pkg/urbit/vere/disk.c b/pkg/urbit/vere/disk.c index 66a8df398..61fe9f754 100644 --- a/pkg/urbit/vere/disk.c +++ b/pkg/urbit/vere/disk.c @@ -500,6 +500,52 @@ u3_disk_read(u3_disk* log_u, c3_d eve_d, c3_d len_d) uv_timer_start(&red_u->tim_u, _disk_read_start_cb, 0, 0); } +struct _cd_list { + u3_disk* log_u; + u3_noun eve; + c3_l mug_l; +}; + +/* _disk_read_list_cb(): lmdb read callback, invoked for each event in order +*/ +static c3_o +_disk_read_list_cb(void* ptr_v, c3_d eve_d, size_t val_i, void* val_p) +{ + struct _cd_list* ven_u = ptr_v; + u3_disk* log_u = ven_u->log_u; + + { + u3_noun job; + c3_l mug_l; + + if ( c3n == u3_disk_sift(log_u, val_i, (c3_y*)val_p, &mug_l, &job) ) { + return c3n; + } + + ven_u->mug_l = mug_l; + ven_u->eve = u3nc(job, ven_u->eve); + } + + return c3y; +} + +/* u3_disk_read_list(): synchronously read a cons list of events. +*/ +u3_weak +u3_disk_read_list(u3_disk* log_u, c3_d eve_d, c3_d len_d, c3_l* mug_l) +{ + struct _cd_list ven_u = { log_u, u3_nul, 0 }; + + if ( c3n == u3_lmdb_read(log_u->mdb_u, &ven_u, + eve_d, len_d, _disk_read_list_cb) ) + { + return u3_none; + } + + *mug_l = ven_u.mug_l; + return u3kb_flop(ven_u.eve); +} + /* u3_disk_walk_init(): init iterator. */ u3_disk_walk* From c15b822087a362f4353ef43de656731483f8406f Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 14 Dec 2022 02:39:53 -0500 Subject: [PATCH 107/136] mars: adds replay implementation --- pkg/urbit/include/vere/mars.h | 22 +++ pkg/urbit/worker/mars.c | 264 ++++++++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 pkg/urbit/include/vere/mars.h create mode 100644 pkg/urbit/worker/mars.c diff --git a/pkg/urbit/include/vere/mars.h b/pkg/urbit/include/vere/mars.h new file mode 100644 index 000000000..a445af0ac --- /dev/null +++ b/pkg/urbit/include/vere/mars.h @@ -0,0 +1,22 @@ +#ifndef U3_VERE_MARS_H +#define U3_VERE_MARS_H + + /** Data types. + **/ + /* u3_mars: the urbit state machine. + */ + typedef struct _u3_mars { + c3_d key_d[4]; // disk key + u3_disk* log_u; // event log + c3_c* dir_c; // execution directory (pier) + c3_d sen_d; // last event requested + c3_d dun_d; // last event processed + c3_l mug_l; // hash of state + } u3_mars; + + /* u3_mars_play(): replay logged events up to [eve_d]. + */ + void + u3_mars_play(u3_mars* mar_u, c3_d eve_d); + +#endif /* ifndef U3_VERE_MARS_H */ diff --git a/pkg/urbit/worker/mars.c b/pkg/urbit/worker/mars.c new file mode 100644 index 000000000..96123db60 --- /dev/null +++ b/pkg/urbit/worker/mars.c @@ -0,0 +1,264 @@ +/* worker/mars.c +** +** the main loop of a mars process. +*/ +#include "all.h" +#include +#include + +/* _mars_step_trace(): initialize or rotate trace file. +*/ +static void +_mars_step_trace(const c3_c* dir_c) +{ + if ( u3C.wag_w & u3o_trace ) { + if ( u3_Host.tra_u.con_w == 0 && u3_Host.tra_u.fun_w == 0 ) { + u3t_trace_open(dir_c); + } + else if ( u3_Host.tra_u.con_w >= 100000 ) { + u3t_trace_close(); + u3t_trace_open(dir_c); + } + } +} + +/* _mars_poke_play(): replay an event. +*/ +static u3_weak +_mars_poke_play(u3_mars* mar_u, c3_d eve_d, u3_noun job) +{ + u3_noun vir; + + if ( c3n == u3v_poke_sure(0, job, &vir) ) { + return vir; + } + + u3z(vir); + return u3_none; +} + +typedef enum { + _play_yes_e, // success + _play_mem_e, // %meme + _play_int_e, // %intr + _play_log_e, // event log fail + _play_mug_e, // mug mismatch + _play_bad_e // total failure +} _mars_play_e; + +/* _mars_play_batch(): replay a batch of events. +*/ +static _mars_play_e +_mars_play_batch(u3_mars* mar_u, c3_o mug_o, c3_w bat_w) +{ + u3_disk* log_u = mar_u->log_u; + u3_disk_walk* wok_u = u3_disk_walk_init(log_u, mar_u->dun_d + 1, bat_w); + u3_fact tac_u; + u3_noun dud; + + while ( c3y == u3_disk_walk_live(wok_u) ) { + if ( c3n == u3_disk_walk_step(wok_u, &tac_u) ) { + u3_disk_walk_done(wok_u); + return _play_log_e; + } + + c3_assert( ++mar_u->sen_d == tac_u.eve_d ); + + if ( u3_none != (dud = _mars_poke_play(mar_u, tac_u.eve_d, tac_u.job)) ) { + c3_m mot_m; + + mar_u->sen_d = mar_u->dun_d; + u3_disk_walk_done(wok_u); + + c3_assert( c3y == u3r_safe_word(u3h(dud), &mot_m) ); + + switch ( mot_m ) { + case c3__meme: { + fprintf(stderr, "play (%" PRIu64 "): %%meme\r\n", tac_u.eve_d); + u3z(dud); + return _play_mem_e; + } + + case c3__intr: { + fprintf(stderr, "play (%" PRIu64 "): %%intr\r\n", tac_u.eve_d); + u3z(dud); + return _play_int_e; + } + + default: { + fprintf(stderr, "play (%" PRIu64 "): failed\r\n", tac_u.eve_d); + u3_pier_punt_goof("play", dud); + // XX say something uplifting + // + return _play_bad_e; + } + } + } + + mar_u->mug_l = u3r_mug(u3A->roc); + + if ( tac_u.mug_l && (mar_u->mug_l != tac_u.mug_l) ) { + fprintf(stderr, "play (%" PRIu64 "): mug mismatch " + "expected %08x, actual %08x\r\n", + tac_u.eve_d, tac_u.mug_l, mar_u->mug_l); + + if ( c3y == mug_o ) { + mar_u->sen_d = mar_u->dun_d; + u3_disk_walk_done(wok_u); + return _play_mug_e; + } + } + + mar_u->dun_d = mar_u->sen_d; + } + + u3_disk_walk_done(wok_u); + + return _play_yes_e; +} + +static c3_o +_mars_do_boot(u3_disk* log_u, c3_d eve_d) +{ + u3_weak eve; + c3_l mug_l; + + if ( u3_none == (eve = u3_disk_read_list(log_u, 1, eve_d, &mug_l)) ) { + fprintf(stderr, "boot: read failed\r\n"); + return c3n; + } + + u3l_log("--------------- bootstrap starting ----------------\r\n"); + + u3l_log("boot: 1-%u\r\n", u3qb_lent(eve)); + + if ( c3n == u3v_boot(eve) ) { + return c3n; + } + + u3l_log("--------------- bootstrap complete ----------------\r\n"); + return c3y; +} + +/* u3_mars_play(): replay logged events up to [eve_d]. +*/ +void +u3_mars_play(u3_mars* mar_u, c3_d eve_d) +{ + u3_disk* log_u = mar_u->log_u; + + if ( !eve_d ) { + eve_d = log_u->dun_d; + } + else if ( eve_d <= mar_u->dun_d ) { + u3l_log("mars: already computed %" PRIu64 "\r\n", eve_d); + u3l_log(" state=%" PRIu64 ", log=%" PRIu64 "\r\n", + mar_u->dun_d, log_u->dun_d); + return; + } + else { + eve_d = c3_min(eve_d, log_u->dun_d); + } + + if ( !mar_u->dun_d ) { + c3_w lif_w; + + if ( c3n == u3_disk_read_meta(log_u, 0, 0, &lif_w) ) { + fprintf(stderr, "mars: disk read meta fail\r\n"); + // XX exit code, cb + // + exit(1); + } + + if ( c3n == _mars_do_boot(mar_u->log_u, lif_w) ) { + fprintf(stderr, "mars: boot fail\r\n"); + // XX exit code, cb + // + exit(1);; + } + + mar_u->sen_d = mar_u->dun_d = lif_w; + } + + if ( mar_u->dun_d == log_u->dun_d ) { + u3l_log("mars: nothing to do!\r\n"); + return; + } + + u3l_log("---------------- playback starting ----------------\r\n"); + + if ( (1ULL + eve_d) == log_u->dun_d ) { + u3l_log("play: event %" PRIu64 "\r\n", log_u->dun_d); + } + else if ( eve_d != log_u->dun_d ) { + u3l_log("play: events %" PRIu64 "-%" PRIu64 " of %" PRIu64 "\r\n", + (c3_d)(1ULL + mar_u->dun_d), + eve_d, + log_u->dun_d); + } + else { + u3l_log("play: events %" PRIu64 "-%" PRIu64 "\r\n", + (c3_d)(1ULL + mar_u->dun_d), + eve_d); + } + + { + c3_d fir_d = mar_u->dun_d; // started at + c3_d mem_d = 0; // last event to meme + c3_w try_w = 0; // [mem_d] retry count + + while ( mar_u->dun_d < eve_d ) { + _mars_step_trace(mar_u->dir_c); + + // XX get batch from args + // + switch ( _mars_play_batch(mar_u, c3y, 1024) ) { + case _play_yes_e: { + u3l_log("play (%" PRIu64 "): done\r\n", mar_u->dun_d); + u3m_reclaim(); + + // XX save a snapshot every N events? + // + } break; + + case _play_mem_e: { + if ( (mem_d == mar_u->dun_d) && (3 == ++try_w) ) { + fprintf(stderr, "play (%" PRIu64 "): failed\r\n", mar_u->dun_d + 1); + u3m_save(); + // XX check loom size, suggest --loom X + // XX exit code, cb + // + exit(1); + } + + mem_d = mar_u->dun_d; + + // XX pack before meld? + // + // if ( u3C.wag_w & u3o_auto_meld ) { + // u3a_print_memory(stderr, "mars: meld: gained", u3u_meld()); + // } + // else { + u3a_print_memory(stderr, "mars: pack: gained", u3m_pack()); + // } + } break; + + // XX handle any specifically? + // + case _play_int_e: + case _play_log_e: + case _play_mug_e: + case _play_bad_e: { + fprintf(stderr, "play (%" PRIu64 "): failed\r\n", mar_u->dun_d + 1); + u3m_save(); + // XX exit code, cb + // + exit(1); + } + } + } + } + + u3l_log("---------------- playback complete ----------------\r\n"); + u3m_save(); +} From 67ef11117fbd8eeb2780bc57b880db4c455061c8 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 14 Dec 2022 02:43:24 -0500 Subject: [PATCH 108/136] vere: updates command docs --- pkg/urbit/daemon/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index 5d7f5c920..7896c69b8 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -644,6 +644,7 @@ _cw_usage(c3_c* bin_c) "utilities:\n", " %s cram %.*s jam state:\n", " %s dock %.*s copy binary:\n", + " %s eval %.*s eval hoon:\n", " %s grab %.*s measure memory usage:\n", " %s info %.*s print pier info:\n", " %s meld %.*s deduplicate snapshot:\n", @@ -652,6 +653,7 @@ _cw_usage(c3_c* bin_c) " %s next %.*s request upgrade:\n", " %s queu %.*s cue state:\n", " %s vere ARGS download binary:\n", + " %s vile %.*s print keyfile:\n", "\n run as a 'serf':\n", " %s serf " #ifdef U3_OS_mingw @@ -2053,6 +2055,7 @@ _cw_utils(c3_i argc, c3_c* argv[]) // $@ ~ :: usage // $% [%cram dir=@t] :: jam state // [%dock dir=@t] :: copy binary + // [%eval ~] :: eval hoon // [?(%grab %mass) dir=@t] :: gc // [%info dir=@t] :: print // [%meld dir=@t] :: deduplicate From 82d5738df448dce91d648cbe2e06b1f69eb67875 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 14 Dec 2022 02:43:11 -0500 Subject: [PATCH 109/136] vere: adds dedicated replay command: play --- pkg/urbit/daemon/main.c | 111 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index 7896c69b8..6a96c7970 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -9,6 +9,7 @@ #include "rsignal.h" #include #include "vere/vere.h" +#include "vere/mars.h" #if !defined(U3_OS_mingw) #include #endif @@ -649,6 +650,7 @@ _cw_usage(c3_c* bin_c) " %s info %.*s print pier info:\n", " %s meld %.*s deduplicate snapshot:\n", " %s pack %.*s defragment snapshot:\n", + " %s play %.*s recompute events:\n", " %s prep %.*s prepare for upgrade:\n", " %s next %.*s request upgrade:\n", " %s queu %.*s cue state:\n", @@ -1768,6 +1770,113 @@ _cw_pack(c3_i argc, c3_c* argv[]) u3m_stop(); } +/* _cw_play_slog(): print during replay. +*/ +static void +_cw_play_slog(u3_noun hod) +{ + u3_pier_tank(0, 0, u3k(u3t(hod))); + u3z(hod); +} + +/* _cw_play(): replay events, but better. +*/ +static void +_cw_play(c3_i argc, c3_c* argv[]) +{ + c3_i ch_i, lid_i; + c3_w arg_w; + + static struct option lop_u[] = { + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, + { "replay-to", required_argument, NULL, 'n' }, + { NULL, 0, NULL, 0 } + }; + + u3_Host.dir_c = _main_pier_run(argv[0]); + + while ( -1 != (ch_i=getopt_long(argc, argv, "n:", lop_u, &lid_i)) ) { + switch ( ch_i ) { + case 6: { // no-demand + u3_Host.ops_u.map = c3n; + u3C.wag_w |= u3o_no_demand; + } break; + + case c3__loom: { + c3_w lom_w; + c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); + if ( (c3n == res_o) || (lom_w < 20) ) { + fprintf(stderr, "error: --loom must be >= 20 and <= %u\r\n", u3a_bits + 2); + exit(1); + } + u3_Host.ops_u.lom_y = lom_w; + } break; + + case 'n': { + u3_Host.ops_u.til_c = strdup(optarg); + break; + } + + case '?': { + fprintf(stderr, "invalid argument\r\n"); + exit(1); + } break; + } + } + + // argv[optind] is always "play" + // + + if ( !u3_Host.dir_c ) { + if ( optind + 1 < argc ) { + u3_Host.dir_c = argv[optind + 1]; + } + else { + fprintf(stderr, "invalid command, pier required\r\n"); + exit(1); + } + + optind++; + } + + if ( optind + 1 != argc ) { + fprintf(stderr, "invalid command\r\n"); + exit(1); + } + + // XX handle SIGTSTP so that the lockfile is not orphaned? + // + u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock + + u3C.wag_w |= u3o_hashless; + u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); + u3C.slog_f = _cw_play_slog; + + { + u3_mars mar_u = { + .log_u = log_u, + .dir_c = u3_Host.dir_c, + .sen_d = u3A->eve_d, + .dun_d = u3A->eve_d, + .mug_l = u3r_mug(u3A->roc) + }; + c3_d eve_d = 0; + c3_c* eve_c = u3_Host.ops_u.til_c; + + if ( u3_Host.ops_u.til_c ) { + if ( 1 != sscanf(eve_c, "%" PRIu64 "", &eve_d) ) { + fprintf(stderr, "mars: replay-to invalid: '%s'\r\n", eve_c); + } + } + + u3_mars_play(&mar_u, eve_d); + } + + u3_disk_exit(log_u); + u3m_stop(); +} + /* _cw_prep(): prepare for upgrade */ static void @@ -2061,6 +2170,7 @@ _cw_utils(c3_i argc, c3_c* argv[]) // [%meld dir=@t] :: deduplicate // [?(%next %upgrade) dir=@t] :: upgrade // [%pack dir=@t] :: defragment + // [%play dir=@t] :: recompute // [%prep dir=@t] :: prep upgrade // [%queu dir=@t eve=@ud] :: cue state // [?(%vere %fetch-vere) dir=@t] :: download vere @@ -2101,6 +2211,7 @@ _cw_utils(c3_i argc, c3_c* argv[]) case c3__meld: _cw_meld(argc, argv); return 1; case c3__next: _cw_next(argc, argv); return 2; // continue on case c3__pack: _cw_pack(argc, argv); return 1; + case c3__play: _cw_play(argc, argv); return 1; case c3__prep: _cw_prep(argc, argv); return 2; // continue on case c3__queu: _cw_queu(argc, argv); return 1; case c3__vere: _cw_vere(argc, argv); return 1; From 3843c6090fa648aff2ff4ca08ade8f99f7bc278a Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 14 Dec 2022 03:01:00 -0500 Subject: [PATCH 110/136] vere: adds full replay option to play subcommand --- pkg/urbit/daemon/main.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index 6a96c7970..e04f9108b 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -10,6 +10,7 @@ #include #include "vere/vere.h" #include "vere/mars.h" +#include "noun/events.h" #if !defined(U3_OS_mingw) #include #endif @@ -1786,17 +1787,19 @@ _cw_play(c3_i argc, c3_c* argv[]) { c3_i ch_i, lid_i; c3_w arg_w; + c3_o ful_o = c3n; static struct option lop_u[] = { { "loom", required_argument, NULL, c3__loom }, { "no-demand", no_argument, NULL, 6 }, - { "replay-to", required_argument, NULL, 'n' }, + { "full", required_argument, NULL, 'f' }, + { "replay-to", no_argument, NULL, 'n' }, { NULL, 0, NULL, 0 } }; u3_Host.dir_c = _main_pier_run(argv[0]); - while ( -1 != (ch_i=getopt_long(argc, argv, "n:", lop_u, &lid_i)) ) { + while ( -1 != (ch_i=getopt_long(argc, argv, "fn:", lop_u, &lid_i)) ) { switch ( ch_i ) { case 6: { // no-demand u3_Host.ops_u.map = c3n; @@ -1813,6 +1816,11 @@ _cw_play(c3_i argc, c3_c* argv[]) u3_Host.ops_u.lom_y = lom_w; } break; + case 'f': { + ful_o = c3y; + break; + } + case 'n': { u3_Host.ops_u.til_c = strdup(optarg); break; @@ -1853,6 +1861,14 @@ _cw_play(c3_i argc, c3_c* argv[]) u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); u3C.slog_f = _cw_play_slog; + if ( c3y == ful_o ) { + u3l_log("mars: preparing for full replay\r\n"); + u3e_yolo(); + u3m_pave(c3y); + u3j_boot(c3y); + u3A->eve_d = 0; + } + { u3_mars mar_u = { .log_u = log_u, From a7880717e3c77c21e185ffe52a4982b0b3bd52d1 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 14 Dec 2022 18:30:09 -0500 Subject: [PATCH 111/136] vere: adds --auto-meld for play subcommand --- pkg/urbit/daemon/main.c | 10 ++++++++++ pkg/urbit/include/noun/options.h | 21 +++++++++++---------- pkg/urbit/worker/mars.c | 10 +++++----- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index e04f9108b..f5c29f909 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -1788,9 +1788,11 @@ _cw_play(c3_i argc, c3_c* argv[]) c3_i ch_i, lid_i; c3_w arg_w; c3_o ful_o = c3n; + c3_o mel_o = c3n; static struct option lop_u[] = { { "loom", required_argument, NULL, c3__loom }, + { "auto-meld", no_argument, NULL, 4 }, { "no-demand", no_argument, NULL, 6 }, { "full", required_argument, NULL, 'f' }, { "replay-to", no_argument, NULL, 'n' }, @@ -1801,6 +1803,10 @@ _cw_play(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "fn:", lop_u, &lid_i)) ) { switch ( ch_i ) { + case 4: { // auto-meld + mel_o = c3y; + } break; + case 6: { // no-demand u3_Host.ops_u.map = c3n; u3C.wag_w |= u3o_no_demand; @@ -1857,6 +1863,10 @@ _cw_play(c3_i argc, c3_c* argv[]) // u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock + if ( c3y == mel_o ) { + u3C.wag_w |= u3o_auto_meld; + } + u3C.wag_w |= u3o_hashless; u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); u3C.slog_f = _cw_play_slog; diff --git a/pkg/urbit/include/noun/options.h b/pkg/urbit/include/noun/options.h index 260b7fd30..26d081f4e 100644 --- a/pkg/urbit/include/noun/options.h +++ b/pkg/urbit/include/noun/options.h @@ -22,16 +22,17 @@ ** _check flags are set inside u3 and heard outside it. */ enum u3o_flag { // execution flags - u3o_debug_ram = 1 << 0, // debug: gc - u3o_debug_cpu = 1 << 1, // debug: profile - u3o_check_corrupt = 1 << 2, // check: gc memory - u3o_check_fatal = 1 << 3, // check: unrecoverable - u3o_verbose = 1 << 4, // be remarkably wordy - u3o_dryrun = 1 << 5, // don't touch checkpoint - u3o_quiet = 1 << 6, // disable ~& - u3o_hashless = 1 << 7, // disable hashboard - u3o_trace = 1 << 8, // enables trace dumping - u3o_no_demand = 1 << 9 // disables demand paging + u3o_debug_ram = 1 << 0, // debug: gc + u3o_debug_cpu = 1 << 1, // debug: profile + u3o_check_corrupt = 1 << 2, // check: gc memory + u3o_check_fatal = 1 << 3, // check: unrecoverable + u3o_verbose = 1 << 4, // be remarkably wordy + u3o_dryrun = 1 << 5, // don't touch checkpoint + u3o_quiet = 1 << 6, // disable ~& + u3o_hashless = 1 << 7, // disable hashboard + u3o_trace = 1 << 8, // enables trace dumping + u3o_auto_meld = 1 << 9, // enables meld under pressure + u3o_no_demand = 1 << 10 // disables demand paging }; /** Globals. diff --git a/pkg/urbit/worker/mars.c b/pkg/urbit/worker/mars.c index 96123db60..43b4a4814 100644 --- a/pkg/urbit/worker/mars.c +++ b/pkg/urbit/worker/mars.c @@ -235,12 +235,12 @@ u3_mars_play(u3_mars* mar_u, c3_d eve_d) // XX pack before meld? // - // if ( u3C.wag_w & u3o_auto_meld ) { - // u3a_print_memory(stderr, "mars: meld: gained", u3u_meld()); - // } - // else { + if ( u3C.wag_w & u3o_auto_meld ) { + u3a_print_memory(stderr, "mars: meld: gained", u3u_meld()); + } + else { u3a_print_memory(stderr, "mars: pack: gained", u3m_pack()); - // } + } } break; // XX handle any specifically? From 8e677537c0ea90e73e3209ef1dc817a1e28ada8d Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 15 Dec 2022 14:10:22 -0500 Subject: [PATCH 112/136] mars: fix bail:meme retry counter --- pkg/urbit/worker/mars.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/urbit/worker/mars.c b/pkg/urbit/worker/mars.c index 43b4a4814..9a3e59ab8 100644 --- a/pkg/urbit/worker/mars.c +++ b/pkg/urbit/worker/mars.c @@ -203,7 +203,6 @@ u3_mars_play(u3_mars* mar_u, c3_d eve_d) } { - c3_d fir_d = mar_u->dun_d; // started at c3_d mem_d = 0; // last event to meme c3_w try_w = 0; // [mem_d] retry count @@ -222,7 +221,11 @@ u3_mars_play(u3_mars* mar_u, c3_d eve_d) } break; case _play_mem_e: { - if ( (mem_d == mar_u->dun_d) && (3 == ++try_w) ) { + if ( mem_d != mar_u->dun_d ) { + mem_d = mar_u->dun_d; + try_w = 0; + } + else if ( 3 == ++try_w ) { fprintf(stderr, "play (%" PRIu64 "): failed\r\n", mar_u->dun_d + 1); u3m_save(); // XX check loom size, suggest --loom X @@ -231,8 +234,6 @@ u3_mars_play(u3_mars* mar_u, c3_d eve_d) exit(1); } - mem_d = mar_u->dun_d; - // XX pack before meld? // if ( u3C.wag_w & u3o_auto_meld ) { From e0320ddfc2c3bb6a6b00306cc916d742ce608718 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 15 Dec 2022 14:18:56 -0500 Subject: [PATCH 113/136] vere: bumps version --- pkg/urbit/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/urbit/version b/pkg/urbit/version index 5dfd5e068..dd93eecb1 100644 --- a/pkg/urbit/version +++ b/pkg/urbit/version @@ -1 +1 @@ -1.14-rc1 \ No newline at end of file +1.14-rc2 \ No newline at end of file From f8394b9e2b8d4a638f684575e65112e29e865483 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 15 Dec 2022 14:37:13 -0500 Subject: [PATCH 114/136] ci: only save version string and setup gcp if we're uploading binaries --- .github/workflows/vere.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/vere.yml b/.github/workflows/vere.yml index eb94e8ea9..b79e41ac2 100644 --- a/.github/workflows/vere.yml +++ b/.github/workflows/vere.yml @@ -125,13 +125,14 @@ jobs: echo -n "$version" > ./version-string - name: upload version string artifact - if: matrix.type == 'linux' + if: inputs.upload && matrix.type == 'linux' uses: actions/upload-artifact@v3 with: name: version-string path: version-string - uses: google-github-actions/auth@v1 + if: inputs.upload with: credentials_json: ${{ secrets.GCP_CREDENTIALS }} From 967f600a7c7710ac63b3f0b6685507c02a4c3a05 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 15 Dec 2022 14:38:04 -0500 Subject: [PATCH 115/136] ci: updates gcp integration for setting release version strings --- .github/workflows/vere.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vere.yml b/.github/workflows/vere.yml index b79e41ac2..262bed278 100644 --- a/.github/workflows/vere.yml +++ b/.github/workflows/vere.yml @@ -245,12 +245,13 @@ jobs: needs: [urbit, mingw] if: inputs.upload steps: - - uses: google-github-actions/setup-gcloud@v0.2.0 + - uses: google-github-actions/auth@v1 + with: + credentials_json: ${{ secrets.GCP_CREDENTIALS }} + + - uses: google-github-actions/setup-gcloud@v1 with: - version: '290.0.1' - service_account_key: ${{ secrets.GCS_SERVICE_ACCOUNT_KEY }} project_id: ${{ secrets.GCS_PROJECT }} - export_default_credentials: true - name: download version-string uses: actions/download-artifact@v3 From 0901538df24374a122d0535e47c7aa24db892770 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 15 Dec 2022 14:42:14 -0500 Subject: [PATCH 116/136] ci: updates gcp integration for uploading tarballs --- .github/workflows/tarballs.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tarballs.yml b/.github/workflows/tarballs.yml index 9f8456cf6..08bd6c806 100644 --- a/.github/workflows/tarballs.yml +++ b/.github/workflows/tarballs.yml @@ -23,12 +23,13 @@ jobs: name: ${{ secrets.CACHIX_NAME }} authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - - uses: google-github-actions/setup-gcloud@v0.2.0 + - uses: google-github-actions/auth@v1 + with: + credentials_json: ${{ secrets.GCP_CREDENTIALS }} + + - uses: google-github-actions/setup-gcloud@v1 with: - version: '290.0.1' - service_account_key: ${{ secrets.GCS_SERVICE_ACCOUNT_KEY }} project_id: ${{ secrets.GCS_PROJECT }} - export_default_credentials: true - run: nix-build -A tarball --arg enableStatic true From fd766faa03937eb7c80f6fe0f713944f3aff85d3 Mon Sep 17 00:00:00 2001 From: Zach Alberico Date: Thu, 15 Dec 2022 13:00:57 -0800 Subject: [PATCH 117/136] Fix build break typo in azimuth Fixes typo introduced in 0e16d82a46ea569e04326ba497b99f93b7ddc751 --- pkg/arvo/app/azimuth.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/app/azimuth.hoon b/pkg/arvo/app/azimuth.hoon index 169139f03..54a97be23 100644 --- a/pkg/arvo/app/azimuth.hoon +++ b/pkg/arvo/app/azimuth.hoon @@ -4,7 +4,7 @@ naive, dice, default-agent, -/ verb, + verb, dbug :: To update, run from dojo: :: -azimuth-snap-state %default 'version-0' From f4235ebc91a38ba4fe8ad2256b4f0305d1a9e602 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 15 Dec 2022 21:22:36 -0500 Subject: [PATCH 118/136] vere: handle ctrl-z like ctrl-c in play command --- pkg/urbit/daemon/main.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index f5c29f909..19e8697f1 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -1780,6 +1780,17 @@ _cw_play_slog(u3_noun hod) u3z(hod); } +/* _cw_play_exit(): exit immediately. +*/ +static void +_cw_play_exit(c3_i int_i) +{ + // explicit fprintf to avoid allocation in u3l_log + // + fprintf(stderr, "\r\n[received keyboard stop signal, exiting]\r\n"); + raise(SIGINT); +} + /* _cw_play(): replay events, but better. */ static void @@ -1859,10 +1870,16 @@ _cw_play(c3_i argc, c3_c* argv[]) exit(1); } - // XX handle SIGTSTP so that the lockfile is not orphaned? - // u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock +#if !defined(U3_OS_mingw) + // Handle SIGTSTP as if it was SIGINT. + // + // Configured here using signal() so as to be immediately available. + // + signal(SIGTSTP, _cw_play_exit); +#endif + if ( c3y == mel_o ) { u3C.wag_w |= u3o_auto_meld; } From f7fd944e0235aeac4721f29ed0f247df29cfe600 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 15 Dec 2022 21:22:54 -0500 Subject: [PATCH 119/136] mars: cleanup event log on exit --- pkg/urbit/worker/mars.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/urbit/worker/mars.c b/pkg/urbit/worker/mars.c index 9a3e59ab8..9e38844cc 100644 --- a/pkg/urbit/worker/mars.c +++ b/pkg/urbit/worker/mars.c @@ -174,7 +174,7 @@ u3_mars_play(u3_mars* mar_u, c3_d eve_d) fprintf(stderr, "mars: boot fail\r\n"); // XX exit code, cb // - exit(1);; + exit(1); } mar_u->sen_d = mar_u->dun_d = lif_w; @@ -231,6 +231,7 @@ u3_mars_play(u3_mars* mar_u, c3_d eve_d) // XX check loom size, suggest --loom X // XX exit code, cb // + u3_disk_exit(log_u); exit(1); } @@ -254,6 +255,7 @@ u3_mars_play(u3_mars* mar_u, c3_d eve_d) u3m_save(); // XX exit code, cb // + u3_disk_exit(log_u); exit(1); } } From 8671d3091d35a1111ceb42e7d6cf2fd1c6bfc4a9 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 15 Dec 2022 21:26:30 -0500 Subject: [PATCH 120/136] u3: make the fault handler robust against initialization reorder --- pkg/urbit/noun/manage.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 02a881d73..876d57f5b 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -1003,10 +1003,19 @@ u3m_flog(c3_w gof_w) void u3m_water(u3_post* low_p, u3_post* hig_p) { + // allow the segfault handler to fire before the road is set + // + // while not explicitly possible in the codebase, + // compiler optimizations can reorder stores + // + if ( !u3R ) { + *low_p = 0; + *hig_p = u3C.wor_i - 1; + } // in a north road, hat points to the end of the heap + 1 word, // while cap points to the top of the stack // - if ( c3y == u3a_is_north(u3R) ) { + else if ( c3y == u3a_is_north(u3R) ) { *low_p = u3R->hat_p - 1; *hig_p = u3R->cap_p; } From 124aec68ad0e7241b9df91c0ead24733b202eaec Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Wed, 16 Nov 2022 00:58:39 -0500 Subject: [PATCH 121/136] term: skip prompt refresh on ^c under -t --- pkg/urbit/vere/io/term.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/urbit/vere/io/term.c b/pkg/urbit/vere/io/term.c index d13bbb9ae..22b4cce1e 100644 --- a/pkg/urbit/vere/io/term.c +++ b/pkg/urbit/vere/io/term.c @@ -1002,7 +1002,9 @@ u3_term_ef_ctlc(void) _term_ovum_plan(uty_u->car_u, wir, cad); } - _term_it_refresh_line(uty_u); + if ( c3n == u3_Host.ops_u.tem ) { + _term_it_refresh_line(uty_u); + } } /* _term_it_put_value(): put numeric color value on lin_w. From 4d08d874c6bd6d16e3ebac8f75578c37a64096e9 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 16 Dec 2022 00:34:15 -0500 Subject: [PATCH 122/136] vere: make full replay (play -f) command bypass corruption --- pkg/urbit/daemon/main.c | 9 ++++- pkg/urbit/include/noun/manage.h | 5 +++ pkg/urbit/noun/manage.c | 68 ++++++++++++++++++--------------- 3 files changed, 49 insertions(+), 33 deletions(-) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index 19e8697f1..0b318630e 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -1885,16 +1885,21 @@ _cw_play(c3_i argc, c3_c* argv[]) } u3C.wag_w |= u3o_hashless; - u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); - u3C.slog_f = _cw_play_slog; if ( c3y == ful_o ) { u3l_log("mars: preparing for full replay\r\n"); + u3m_init((size_t)1 << u3_Host.ops_u.lom_y); + u3e_live(u3m_pier(u3_Host.dir_c)); u3e_yolo(); u3m_pave(c3y); u3j_boot(c3y); u3A->eve_d = 0; } + else { + u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); + } + + u3C.slog_f = _cw_play_slog; { u3_mars mar_u = { diff --git a/pkg/urbit/include/noun/manage.h b/pkg/urbit/include/noun/manage.h index 2d9fb1822..0ff5b504f 100644 --- a/pkg/urbit/include/noun/manage.h +++ b/pkg/urbit/include/noun/manage.h @@ -8,6 +8,11 @@ c3_d u3m_boot(c3_c* dir_c, size_t len_i); + /* u3m_pier(): make a pier. + */ + c3_c* + u3m_pier(c3_c* dir_c); + /* u3m_boot_lite(): start without checkpointing. */ c3_d diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 876d57f5b..70aa1d14c 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -1961,6 +1961,42 @@ u3m_stop() c3_free(u3D.ray_u); } +/* u3m_pier(): make a pier. +*/ +c3_c* +u3m_pier(c3_c* dir_c) +{ + c3_c ful_c[8193]; + + u3C.dir_c = dir_c; + + snprintf(ful_c, 8192, "%s", dir_c); + if ( c3_mkdir(ful_c, 0700) ) { + if ( EEXIST != errno ) { + fprintf(stderr, "loom: pier create: %s\r\n", strerror(errno)); + c3_assert(0); + } + } + + snprintf(ful_c, 8192, "%s/.urb", dir_c); + if ( c3_mkdir(ful_c, 0700) ) { + if ( EEXIST != errno ) { + fprintf(stderr, "loom: .urb create: %s\r\n", strerror(errno)); + c3_assert(0); + } + } + + snprintf(ful_c, 8192, "%s/.urb/chk", dir_c); + if ( c3_mkdir(ful_c, 0700) ) { + if ( EEXIST != errno ) { + fprintf(stderr, "loom: .urb/chk create: %s\r\n", strerror(errno)); + c3_assert(0); + } + } + + return strdup(ful_c); +} + /* u3m_boot(): start the u3 system. return next event, starting from 1. */ c3_d @@ -1968,43 +2004,13 @@ u3m_boot(c3_c* dir_c, size_t len_i) { c3_o nuu_o; - u3C.dir_c = dir_c; - /* Activate the loom. */ u3m_init(len_i); /* Activate the storage system. */ - { - c3_c ful_c[8193]; - - snprintf(ful_c, 8192, "%s", dir_c); - if ( c3_mkdir(ful_c, 0700) ) { - if ( EEXIST != errno ) { - fprintf(stderr, "loom: pier create: %s\r\n", strerror(errno)); - c3_assert(0); - } - } - - snprintf(ful_c, 8192, "%s/.urb", dir_c); - if ( c3_mkdir(ful_c, 0700) ) { - if ( EEXIST != errno ) { - fprintf(stderr, "loom: .urb create: %s\r\n", strerror(errno)); - c3_assert(0); - } - } - - snprintf(ful_c, 8192, "%s/.urb/chk", dir_c); - if ( c3_mkdir(ful_c, 0700) ) { - if ( EEXIST != errno ) { - fprintf(stderr, "loom: .urb/chk create: %s\r\n", strerror(errno)); - c3_assert(0); - } - } - - nuu_o = u3e_live(strdup(ful_c)); - } + nuu_o = u3e_live(u3m_pier(dir_c)); /* Activate tracing. */ From 4b5494fb85c29cddf2d4a8dc663c42b33198fd77 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 16 Dec 2022 00:34:54 -0500 Subject: [PATCH 123/136] vere: fix "queu" command argument parsing --- pkg/urbit/daemon/main.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index 0b318630e..2a449e911 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -1472,12 +1472,14 @@ _cw_cram(c3_i argc, c3_c* argv[]) static void _cw_queu(c3_i argc, c3_c* argv[]) { - c3_i ch_i, lid_i; - c3_w arg_w; + c3_i ch_i, lid_i; + c3_w arg_w; + c3_c* roc_c = 0; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, + { "replay-from", required_argument, NULL, 'r' }, { NULL, 0, NULL, 0 } }; @@ -1500,6 +1502,10 @@ _cw_queu(c3_i argc, c3_c* argv[]) u3_Host.ops_u.lom_y = lom_w; } break; + case 'r': { + roc_c = strdup(optarg); + } break; + case '?': { fprintf(stderr, "invalid argument\r\n"); exit(1); @@ -1507,9 +1513,13 @@ _cw_queu(c3_i argc, c3_c* argv[]) } } + if ( !roc_c ) { + fprintf(stderr, "invalid command, -r $EVENT required\r\n"); + exit(1); + } + // argv[optind] is always "queu" // - if ( !u3_Host.dir_c ) { if ( optind + 1 < argc ) { u3_Host.dir_c = argv[optind + 1]; @@ -1527,11 +1537,10 @@ _cw_queu(c3_i argc, c3_c* argv[]) exit(1); } - c3_c* eve_c; - c3_d eve_d; + c3_d eve_d; - if ( 1 != sscanf(eve_c, "%" PRIu64 "", &eve_d) ) { - fprintf(stderr, "urbit: queu: invalid number '%s'\r\n", eve_c); + if ( 1 != sscanf(roc_c, "%" PRIu64 "", &eve_d) ) { + fprintf(stderr, "urbit: queu: invalid number '%s'\r\n", roc_c); exit(1); } else { From aa83de5e8bc30f55c1ce4a02d682e945558403da Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 16 Dec 2022 00:52:19 -0500 Subject: [PATCH 124/136] vere: bumps version --- pkg/urbit/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/urbit/version b/pkg/urbit/version index dd93eecb1..64349e8f7 100644 --- a/pkg/urbit/version +++ b/pkg/urbit/version @@ -1 +1 @@ -1.14-rc2 \ No newline at end of file +1.14-rc3 \ No newline at end of file From 1f84fcf262d18701b1e497529c1e41902a9c08a6 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 16 Dec 2022 10:00:22 -0500 Subject: [PATCH 125/136] vere: fix queu short-args parsing --- pkg/urbit/daemon/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index 2a449e911..d29da9824 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -1485,7 +1485,7 @@ _cw_queu(c3_i argc, c3_c* argv[]) u3_Host.dir_c = _main_pier_run(argv[0]); - while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { + while ( -1 != (ch_i=getopt_long(argc, argv, "r:", lop_u, &lid_i)) ) { switch ( ch_i ) { case 6: { // no-demand u3_Host.ops_u.map = c3n; From 4e9de85abae3347027766a3a6010ae2c9641e45b Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 16 Dec 2022 11:17:53 -0500 Subject: [PATCH 126/136] vere: refactors http server init for better error messages --- pkg/urbit/vere/io/http.c | 112 ++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/pkg/urbit/vere/io/http.c b/pkg/urbit/vere/io/http.c index a564c46ce..0336cdca2 100644 --- a/pkg/urbit/vere/io/http.c +++ b/pkg/urbit/vere/io/http.c @@ -1504,6 +1504,61 @@ _http_serv_init_h2o(SSL_CTX* tls_u, c3_o log, c3_o red) return h2o_u; } +/* _http_serv_start_err(): handle errors in starting http server. +*/ +static c3_i +_http_serv_start_err(const c3_c* cap_c, u3_http* htp_u, c3_i sas_i) +{ + u3_pier* pir_u = htp_u->htd_u->car_u.pir_u; + + if ( UV_EADDRNOTAVAIL == sas_i ) { + u3l_log("http: unavailable ip address %s\r\n", u3_Host.ops_u.bin_c); + u3_king_bail(); + return 0; + } + + // ports specified, no incrementing/retry + // + if ( c3y == htp_u->dis ) { + u3l_log("http: %s (%" PRIu16 "): %s\n", + cap_c, htp_u->por_s, uv_strerror(sas_i)); + u3_king_bail(); + return 0; + } + + // increment and retry + // + if ( (UV_EADDRINUSE == sas_i) || (UV_EACCES == sas_i) ) { + if ( (c3y == htp_u->sec) && (443 == htp_u->por_s) ) { + htp_u->por_s = 8443; + } + else if ( (c3n == htp_u->sec) && (80 == htp_u->por_s) ) { + htp_u->por_s = 8080; + } + else { + htp_u->por_s++; + // XX + // + if ( c3n == htp_u->lop ) { + if ( c3y == htp_u->sec ) { + pir_u->pes_s = htp_u->por_s; + } + else { + pir_u->per_s = htp_u->por_s; + } + } + } + + return 1; + } + + // total failure XX bail? + // + u3l_log("http: %s: %s\n", cap_c, uv_strerror(sas_i)); + _http_serv_free(htp_u); + return 0; +} + /* _http_serv_start(): start http server. */ static void @@ -1519,10 +1574,12 @@ _http_serv_start(u3_http* htp_u) INADDR_ANY; if ( 0 != u3_Host.ops_u.bin_c && c3n == htp_u->lop ) { + // already validated in arguments parser + // inet_pton(AF_INET, u3_Host.ops_u.bin_c, &adr_u.sin_addr); } - uv_tcp_init(u3L, &htp_u->wax_u); + c3_assert( !uv_tcp_init(u3L, &htp_u->wax_u) ); /* Try ascending ports. */ @@ -1532,46 +1589,25 @@ _http_serv_start(u3_http* htp_u) adr_u.sin_port = htons(htp_u->por_s); if ( 0 != (sas_i = uv_tcp_bind(&htp_u->wax_u, - (const struct sockaddr*)&adr_u, 0)) || - 0 != (sas_i = uv_listen((uv_stream_t*)&htp_u->wax_u, - TCP_BACKLOG, _http_serv_listen_cb)) ) { - if ( UV_EADDRNOTAVAIL == sas_i ) { - u3l_log("http: ip address not available\n"); - u3_king_bail(); - } - if ( c3y == htp_u->dis ) { - u3l_log("http: listen (%" PRIu16 "): %s\n", htp_u->por_s, - uv_strerror(sas_i)); - u3_king_bail(); - } - if ( (UV_EADDRINUSE == sas_i) || (UV_EACCES == sas_i) ) { - if ( (c3y == htp_u->sec) && (443 == htp_u->por_s) ) { - htp_u->por_s = 8443; - } - else if ( (c3n == htp_u->sec) && (80 == htp_u->por_s) ) { - htp_u->por_s = 8080; - } - else { - htp_u->por_s++; - // XX - // - if ( c3n == htp_u->lop ) { - if ( c3y == htp_u->sec ) { - pir_u->pes_s = htp_u->por_s; - } - else { - pir_u->per_s = htp_u->por_s; - } - } - } - + (const struct sockaddr*)&adr_u, 0)) ) + { + if ( _http_serv_start_err("bind", htp_u, sas_i) ) { continue; } + else { + return; + } + } - u3l_log("http: listen: %s\n", uv_strerror(sas_i)); - - _http_serv_free(htp_u); - return; + if ( 0 != (sas_i = uv_listen((uv_stream_t*)&htp_u->wax_u, + TCP_BACKLOG, _http_serv_listen_cb)) ) + { + if ( _http_serv_start_err("listen", htp_u, sas_i) ) { + continue; + } + else { + return; + } } u3l_log("http: %s live on %s://localhost:%d\n", From eb324b4fa4c2b08c76317601c88708c624f98c70 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Tue, 20 Dec 2022 18:55:39 -0500 Subject: [PATCH 127/136] vere: bumps version --- pkg/urbit/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/urbit/version b/pkg/urbit/version index 9f76d37b7..07fe6f6c9 100644 --- a/pkg/urbit/version +++ b/pkg/urbit/version @@ -1 +1 @@ -1.13 \ No newline at end of file +1.15 \ No newline at end of file From 56ceee500611da066261d95bd5903a6a26f7e071 Mon Sep 17 00:00:00 2001 From: Philip Monk Date: Tue, 3 Jan 2023 11:24:54 -0700 Subject: [PATCH 128/136] Revert "Merge pull request #6171 from urbit/jb/release/vere" This reverts commit 21f70231981eab8ed8250254c507dc99b330d857, reversing changes made to 1e09188065f005a5446cc104bc982b1808607a18. --- .github/workflows/tarballs.yml | 9 +- .github/workflows/vere.yml | 12 +- pkg/urbit/Makefile | 3 +- pkg/urbit/bench/ur_bench.c | 4 +- pkg/urbit/c/defs.c | 96 -- pkg/urbit/compat/mingw/compat.c | 57 -- pkg/urbit/compat/mingw/compat.h | 2 - pkg/urbit/compat/mingw/seh_handler.c | 2 +- pkg/urbit/daemon/main.c | 277 +---- pkg/urbit/daemon/whereami.c | 207 +--- pkg/urbit/daemon/whereami.h | 6 +- pkg/urbit/include/all.h | 1 + pkg/urbit/include/c/defs.h | 29 - pkg/urbit/include/noun/allocate.h | 5 - pkg/urbit/include/noun/events.h | 28 +- pkg/urbit/include/noun/manage.h | 22 +- pkg/urbit/include/noun/options.h | 20 +- pkg/urbit/include/noun/urth.h | 2 +- pkg/urbit/include/noun/vortex.h | 5 - pkg/urbit/include/vere/db/lmdb.h | 29 - pkg/urbit/include/vere/mars.h | 22 - pkg/urbit/include/vere/vere.h | 54 +- pkg/urbit/noun/allocate.c | 136 +-- pkg/urbit/noun/events.c | 963 ++++++++---------- pkg/urbit/noun/manage.c | 167 +-- pkg/urbit/noun/trace.c | 17 +- pkg/urbit/noun/urth.c | 67 +- pkg/urbit/noun/vortex.c | 69 +- pkg/urbit/tests/ames_tests.c | 3 +- pkg/urbit/tests/events_tests.c | 251 ----- pkg/urbit/tests/hashtable_tests.c | 3 +- pkg/urbit/tests/jam_tests.c | 4 +- pkg/urbit/tests/jet_tests.c | 3 +- pkg/urbit/tests/mug_tests.c | 3 +- pkg/urbit/tests/newt_tests.c | 3 +- .../tests/{meme_tests.c => nock_tests.c} | 15 +- pkg/urbit/tests/noun_tests.c | 3 +- pkg/urbit/vere/db/lmdb.c | 106 -- pkg/urbit/vere/disk.c | 227 +---- pkg/urbit/vere/io/http.c | 145 +-- pkg/urbit/vere/io/term.c | 58 +- pkg/urbit/vere/io/unix.c | 55 +- pkg/urbit/vere/king.c | 90 +- pkg/urbit/vere/lord.c | 2 +- pkg/urbit/version | 2 +- pkg/urbit/worker/mars.c | 267 ----- pkg/urbit/worker/serf.c | 143 ++- 47 files changed, 986 insertions(+), 2708 deletions(-) delete mode 100644 pkg/urbit/c/defs.c delete mode 100644 pkg/urbit/include/vere/mars.h delete mode 100644 pkg/urbit/tests/events_tests.c rename pkg/urbit/tests/{meme_tests.c => nock_tests.c} (80%) delete mode 100644 pkg/urbit/worker/mars.c diff --git a/.github/workflows/tarballs.yml b/.github/workflows/tarballs.yml index 08bd6c806..9f8456cf6 100644 --- a/.github/workflows/tarballs.yml +++ b/.github/workflows/tarballs.yml @@ -23,13 +23,12 @@ jobs: name: ${{ secrets.CACHIX_NAME }} authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - - uses: google-github-actions/auth@v1 - with: - credentials_json: ${{ secrets.GCP_CREDENTIALS }} - - - uses: google-github-actions/setup-gcloud@v1 + - uses: google-github-actions/setup-gcloud@v0.2.0 with: + version: '290.0.1' + service_account_key: ${{ secrets.GCS_SERVICE_ACCOUNT_KEY }} project_id: ${{ secrets.GCS_PROJECT }} + export_default_credentials: true - run: nix-build -A tarball --arg enableStatic true diff --git a/.github/workflows/vere.yml b/.github/workflows/vere.yml index 262bed278..eb94e8ea9 100644 --- a/.github/workflows/vere.yml +++ b/.github/workflows/vere.yml @@ -125,14 +125,13 @@ jobs: echo -n "$version" > ./version-string - name: upload version string artifact - if: inputs.upload && matrix.type == 'linux' + if: matrix.type == 'linux' uses: actions/upload-artifact@v3 with: name: version-string path: version-string - uses: google-github-actions/auth@v1 - if: inputs.upload with: credentials_json: ${{ secrets.GCP_CREDENTIALS }} @@ -245,13 +244,12 @@ jobs: needs: [urbit, mingw] if: inputs.upload steps: - - uses: google-github-actions/auth@v1 - with: - credentials_json: ${{ secrets.GCP_CREDENTIALS }} - - - uses: google-github-actions/setup-gcloud@v1 + - uses: google-github-actions/setup-gcloud@v0.2.0 with: + version: '290.0.1' + service_account_key: ${{ secrets.GCS_SERVICE_ACCOUNT_KEY }} project_id: ${{ secrets.GCS_PROJECT }} + export_default_credentials: true - name: download version-string uses: actions/download-artifact@v3 diff --git a/pkg/urbit/Makefile b/pkg/urbit/Makefile index f86731275..6b934bc79 100644 --- a/pkg/urbit/Makefile +++ b/pkg/urbit/Makefile @@ -2,7 +2,6 @@ include config.mk compat_mks := $(foreach dir,$(compat),$(wildcard compat/$(dir)/*.mk)) include $(compat_mks) -c = $(wildcard c/*.c) jets = jets/tree.c $(wildcard jets/*/*.c) noun = $(wildcard noun/*.c) ur = $(wildcard ur/*.c) @@ -14,7 +13,7 @@ bench = $(wildcard bench/*.c) compat := $(foreach dir,$(compat),$(wildcard compat/$(dir)/*.c)) -common = $(c) $(jets) $(noun) $(ur) $(vere) $(compat) +common = $(jets) $(noun) $(ur) $(vere) $(compat) headers = $(shell find include -type f) common_objs = $(shell echo $(common) | sed 's/\.c/.o/g') diff --git a/pkg/urbit/bench/ur_bench.c b/pkg/urbit/bench/ur_bench.c index 91f04bbbf..4c0151357 100644 --- a/pkg/urbit/bench/ur_bench.c +++ b/pkg/urbit/bench/ur_bench.c @@ -7,7 +7,9 @@ static void _setup(void) { - u3m_boot_lite(1 << 24); + u3m_init(1 << 24); + u3m_pave(c3y); + u3e_init(); } /* _ames_writ_ex(): |hi packet from fake ~zod to fake ~nec diff --git a/pkg/urbit/c/defs.c b/pkg/urbit/c/defs.c deleted file mode 100644 index 93a1d9b89..000000000 --- a/pkg/urbit/c/defs.c +++ /dev/null @@ -1,96 +0,0 @@ -/// @file defs.c - -#include "c/defs.h" - -/* c3_pread(): full positioned read(), up to eof, retrying errors. -*/ -ssize_t -c3_pread(c3_i fid_i, void* buf_v, size_t len_i, off_t off_i) -{ - c3_w max_w = 128; - c3_w try_w = 0; - size_t rem_i = len_i; - ssize_t ret_i; - - do { - if ( (0 > (ret_i = pread(fid_i, buf_v, rem_i, off_i))) - && ( (++try_w == max_w) - || ( (EINTR != errno) - && (EAGAIN != errno) - && (EWOULDBLOCK != errno) ))) - { - return -1; - } - else if ( 0 == ret_i ) { - break; - } - else { - buf_v = (void*)((c3_c*)buf_v + ret_i); - rem_i -= ret_i; - off_i += ret_i; - } - - } - while ( rem_i ); - - return len_i - rem_i; -} - -/* c3_pwrite(): full positioned write(), retrying errors. -*/ -ssize_t -c3_pwrite(c3_i fid_i, const void* buf_v, size_t len_i, off_t off_i) -{ - c3_w max_w = 128; - c3_w try_w = 0; - size_t rem_i = len_i; - ssize_t ret_i; - - do { - if ( (0 > (ret_i = pwrite(fid_i, buf_v, rem_i, off_i))) - && ( (++try_w == max_w) - || ( (EINTR != errno) - && (EAGAIN != errno) - && (EWOULDBLOCK != errno) ))) - { - return -1; - } - else { - buf_v = (void*)((c3_c*)buf_v + ret_i); - rem_i -= ret_i; - off_i += ret_i; - } - } - while ( rem_i ); - - return len_i; -} - -/* c3_write(): full write(), retrying errors. -*/ -ssize_t -c3_write(c3_i fid_i, const void* buf_v, size_t len_i) -{ - c3_w max_w = 128; - c3_w try_w = 0; - size_t rem_i = len_i; - ssize_t ret_i; - - do { - if ( (0 > (ret_i = write(fid_i, buf_v, rem_i))) - && ( (++try_w == max_w) - || ( (EINTR != errno) - && (EAGAIN != errno) - && (EWOULDBLOCK != errno) ))) - { - return -1; - } - else { - buf_v = (void*)((c3_c*)buf_v + ret_i); - rem_i -= ret_i; - } - } - while ( rem_i ); - - return len_i; -} diff --git a/pkg/urbit/compat/mingw/compat.c b/pkg/urbit/compat/mingw/compat.c index 1bd142128..ebef5041f 100644 --- a/pkg/urbit/compat/mingw/compat.c +++ b/pkg/urbit/compat/mingw/compat.c @@ -144,63 +144,6 @@ int link(const char *path1, const char *path2) return -1; } -ssize_t pread(int fd, void *buf, size_t count, off_t offset) -{ - DWORD len = 0; - - OVERLAPPED overlapped = {0}; - - overlapped.OffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ? - (DWORD)0 : (DWORD)((offset >> 32) & 0xFFFFFFFFL); - overlapped.Offset = (sizeof(off_t) <= sizeof(DWORD)) ? - (DWORD)offset : (DWORD)(offset & 0xFFFFFFFFL); - - HANDLE h = (HANDLE)_get_osfhandle(fd); - - if ( INVALID_HANDLE_VALUE == h ) { - errno = EBADF; - return -1; - } - - if ( !ReadFile(h, buf, count, &len, &overlapped) ) { - DWORD err = GetLastError(); - - if ( ERROR_HANDLE_EOF != err ) { - errno = err_win_to_posix(err); - return -1; - } - } - - return (ssize_t)len; -} - -ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) -{ - DWORD len = 0; - - OVERLAPPED overlapped = {0}; - - overlapped.OffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ? - (DWORD)0 : (DWORD)((offset >> 32) & 0xFFFFFFFFL); - overlapped.Offset = (sizeof(off_t) <= sizeof(DWORD)) ? - (DWORD)offset : (DWORD)(offset & 0xFFFFFFFFL); - - HANDLE h = (HANDLE)_get_osfhandle(fd); - - if ( INVALID_HANDLE_VALUE == h ) { - errno = EBADF; - return -1; - } - - if ( !WriteFile(h, buf, count, &len, &overlapped) ) { - errno = err_win_to_posix(GetLastError()); - return -1; - } - - return (ssize_t)len; -} - - // from msys2 mingw-packages-dev patches // ----------------------------------------------------------------------- diff --git a/pkg/urbit/compat/mingw/compat.h b/pkg/urbit/compat/mingw/compat.h index 80809f54b..56c35e90a 100644 --- a/pkg/urbit/compat/mingw/compat.h +++ b/pkg/urbit/compat/mingw/compat.h @@ -4,8 +4,6 @@ #define mkdir(A, B) mkdir(A) int link(const char *path1, const char *path2); -ssize_t pread(int fd, void *buf, size_t count, off_t offset); -ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); char *realpath(const char *path, char *resolved_path); int fdatasync(int fd); int utimes(const char *path, const struct timeval times[2]); diff --git a/pkg/urbit/compat/mingw/seh_handler.c b/pkg/urbit/compat/mingw/seh_handler.c index 6730435e4..ecf59f0a4 100644 --- a/pkg/urbit/compat/mingw/seh_handler.c +++ b/pkg/urbit/compat/mingw/seh_handler.c @@ -12,7 +12,7 @@ EXCEPTION_DISPOSITION _mingw_exception_filter( { if (ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && ExceptionRecord->ExceptionInformation[0] == 1 && - u3m_fault((void*)ExceptionRecord->ExceptionInformation[1], 1)) + u3e_fault((void*)ExceptionRecord->ExceptionInformation[1], 1)) { return ExceptionContinueExecution; } diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index d29da9824..1853ed871 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -9,8 +9,6 @@ #include "rsignal.h" #include #include "vere/vere.h" -#include "vere/mars.h" -#include "noun/events.h" #if !defined(U3_OS_mingw) #include #endif @@ -155,15 +153,6 @@ _main_init(void) // u3_Host.ops_u.has = c3y; - // demand paging (ie, file-backed mapping for the loom) - // is not yet supported on windows - // -#ifdef U3_OS_mingw - u3_Host.ops_u.map = c3n; -#else - u3_Host.ops_u.map = c3y; -#endif - u3_Host.ops_u.net = c3y; u3_Host.ops_u.lit = c3n; u3_Host.ops_u.nuu = c3n; @@ -257,7 +246,6 @@ _main_getopt(c3_i argc, c3_c** argv) { "scry-format", required_argument, NULL, 'Z' }, // { "urth-loom", required_argument, NULL, 5 }, - { "no-demand", no_argument, NULL, 6 }, // { NULL, 0, NULL, 0 }, }; @@ -278,10 +266,6 @@ _main_getopt(c3_i argc, c3_c** argv) u3_Host.ops_u.lut_y = lut_w; break; } - case 6: { // no-demand - u3_Host.ops_u.map = c3n; - break; - } case 'X': { u3_Host.ops_u.pek_c = strdup(optarg); break; @@ -646,17 +630,14 @@ _cw_usage(c3_c* bin_c) "utilities:\n", " %s cram %.*s jam state:\n", " %s dock %.*s copy binary:\n", - " %s eval %.*s eval hoon:\n", " %s grab %.*s measure memory usage:\n", " %s info %.*s print pier info:\n", " %s meld %.*s deduplicate snapshot:\n", " %s pack %.*s defragment snapshot:\n", - " %s play %.*s recompute events:\n", " %s prep %.*s prepare for upgrade:\n", " %s next %.*s request upgrade:\n", " %s queu %.*s cue state:\n", " %s vere ARGS download binary:\n", - " %s vile %.*s print keyfile:\n", "\n run as a 'serf':\n", " %s serf " #ifdef U3_OS_mingw @@ -1391,8 +1372,7 @@ _cw_cram(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, + { "loom", required_argument, NULL, c3__loom }, { NULL, 0, NULL, 0 } }; @@ -1400,11 +1380,6 @@ _cw_cram(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { - case 6: { // no-demand - u3_Host.ops_u.map = c3n; - u3C.wag_w |= u3o_no_demand; - } break; - case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -1457,7 +1432,7 @@ _cw_cram(c3_i argc, c3_c* argv[]) // save even on failure, as we just did all the work of deduplication // - u3m_save(); + u3e_save(); u3_disk_exit(log_u); if ( c3n == ret_o ) { @@ -1472,26 +1447,18 @@ _cw_cram(c3_i argc, c3_c* argv[]) static void _cw_queu(c3_i argc, c3_c* argv[]) { - c3_i ch_i, lid_i; - c3_w arg_w; - c3_c* roc_c = 0; + c3_i ch_i, lid_i; + c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, - { "replay-from", required_argument, NULL, 'r' }, + { "loom", required_argument, NULL, c3__loom }, { NULL, 0, NULL, 0 } }; u3_Host.dir_c = _main_pier_run(argv[0]); - while ( -1 != (ch_i=getopt_long(argc, argv, "r:", lop_u, &lid_i)) ) { + while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { - case 6: { // no-demand - u3_Host.ops_u.map = c3n; - u3C.wag_w |= u3o_no_demand; - } break; - case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -1502,10 +1469,6 @@ _cw_queu(c3_i argc, c3_c* argv[]) u3_Host.ops_u.lom_y = lom_w; } break; - case 'r': { - roc_c = strdup(optarg); - } break; - case '?': { fprintf(stderr, "invalid argument\r\n"); exit(1); @@ -1513,13 +1476,9 @@ _cw_queu(c3_i argc, c3_c* argv[]) } } - if ( !roc_c ) { - fprintf(stderr, "invalid command, -r $EVENT required\r\n"); - exit(1); - } - // argv[optind] is always "queu" // + if ( !u3_Host.dir_c ) { if ( optind + 1 < argc ) { u3_Host.dir_c = argv[optind + 1]; @@ -1537,10 +1496,11 @@ _cw_queu(c3_i argc, c3_c* argv[]) exit(1); } - c3_d eve_d; + c3_c* eve_c; + c3_d eve_d; - if ( 1 != sscanf(roc_c, "%" PRIu64 "", &eve_d) ) { - fprintf(stderr, "urbit: queu: invalid number '%s'\r\n", roc_c); + if ( 1 != sscanf(eve_c, "%" PRIu64 "", &eve_d) ) { + fprintf(stderr, "urbit: queu: invalid number '%s'\r\n", eve_c); exit(1); } else { @@ -1559,7 +1519,7 @@ _cw_queu(c3_i argc, c3_c* argv[]) exit(1); } - u3m_save(); + u3e_save(); u3_disk_exit(log_u); fprintf(stderr, "urbit: queu: rock loaded at event %" PRIu64 "\r\n", eve_d); @@ -1576,8 +1536,7 @@ _cw_meld(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, + { "loom", required_argument, NULL, c3__loom }, { NULL, 0, NULL, 0 } }; @@ -1585,11 +1544,6 @@ _cw_meld(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { - case 6: { // no-demand - u3_Host.ops_u.map = c3n; - u3C.wag_w |= u3o_no_demand; - } break; - case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -1633,9 +1587,11 @@ _cw_meld(c3_i argc, c3_c* argv[]) u3C.wag_w |= u3o_hashless; u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); - u3a_print_memory(stderr, "urbit: meld: gained", u3u_meld()); + pre_w = u3a_open(u3R); + u3u_meld(); + u3a_print_memory(stderr, "urbit: meld: gained", (u3a_open(u3R) - pre_w)); - u3m_save(); + u3e_save(); u3_disk_exit(log_u); u3m_stop(); } @@ -1649,9 +1605,8 @@ _cw_next(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "arch", required_argument, NULL, 'a' }, - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, + { "arch", required_argument, NULL, 'a' }, + { "loom", required_argument, NULL, c3__loom }, { NULL, 0, NULL, 0 } }; @@ -1663,11 +1618,6 @@ _cw_next(c3_i argc, c3_c* argv[]) u3_Host.arc_c = strdup(optarg); } break; - case 6: { // no-demand - u3_Host.ops_u.map = c3n; - u3C.wag_w |= u3o_no_demand; - } break; - case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -1719,8 +1669,7 @@ _cw_pack(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, + { "loom", required_argument, NULL, c3__loom }, { NULL, 0, NULL, 0 } }; @@ -1728,11 +1677,6 @@ _cw_pack(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { - case 6: { // no-demand - u3_Host.ops_u.map = c3n; - u3C.wag_w |= u3o_no_demand; - } break; - case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -1775,161 +1719,7 @@ _cw_pack(c3_i argc, c3_c* argv[]) u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); u3a_print_memory(stderr, "urbit: pack: gained", u3m_pack()); - u3m_save(); - u3_disk_exit(log_u); - u3m_stop(); -} - -/* _cw_play_slog(): print during replay. -*/ -static void -_cw_play_slog(u3_noun hod) -{ - u3_pier_tank(0, 0, u3k(u3t(hod))); - u3z(hod); -} - -/* _cw_play_exit(): exit immediately. -*/ -static void -_cw_play_exit(c3_i int_i) -{ - // explicit fprintf to avoid allocation in u3l_log - // - fprintf(stderr, "\r\n[received keyboard stop signal, exiting]\r\n"); - raise(SIGINT); -} - -/* _cw_play(): replay events, but better. -*/ -static void -_cw_play(c3_i argc, c3_c* argv[]) -{ - c3_i ch_i, lid_i; - c3_w arg_w; - c3_o ful_o = c3n; - c3_o mel_o = c3n; - - static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "auto-meld", no_argument, NULL, 4 }, - { "no-demand", no_argument, NULL, 6 }, - { "full", required_argument, NULL, 'f' }, - { "replay-to", no_argument, NULL, 'n' }, - { NULL, 0, NULL, 0 } - }; - - u3_Host.dir_c = _main_pier_run(argv[0]); - - while ( -1 != (ch_i=getopt_long(argc, argv, "fn:", lop_u, &lid_i)) ) { - switch ( ch_i ) { - case 4: { // auto-meld - mel_o = c3y; - } break; - - case 6: { // no-demand - u3_Host.ops_u.map = c3n; - u3C.wag_w |= u3o_no_demand; - } break; - - case c3__loom: { - c3_w lom_w; - c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); - if ( (c3n == res_o) || (lom_w < 20) ) { - fprintf(stderr, "error: --loom must be >= 20 and <= %u\r\n", u3a_bits + 2); - exit(1); - } - u3_Host.ops_u.lom_y = lom_w; - } break; - - case 'f': { - ful_o = c3y; - break; - } - - case 'n': { - u3_Host.ops_u.til_c = strdup(optarg); - break; - } - - case '?': { - fprintf(stderr, "invalid argument\r\n"); - exit(1); - } break; - } - } - - // argv[optind] is always "play" - // - - if ( !u3_Host.dir_c ) { - if ( optind + 1 < argc ) { - u3_Host.dir_c = argv[optind + 1]; - } - else { - fprintf(stderr, "invalid command, pier required\r\n"); - exit(1); - } - - optind++; - } - - if ( optind + 1 != argc ) { - fprintf(stderr, "invalid command\r\n"); - exit(1); - } - - u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock - -#if !defined(U3_OS_mingw) - // Handle SIGTSTP as if it was SIGINT. - // - // Configured here using signal() so as to be immediately available. - // - signal(SIGTSTP, _cw_play_exit); -#endif - - if ( c3y == mel_o ) { - u3C.wag_w |= u3o_auto_meld; - } - - u3C.wag_w |= u3o_hashless; - - if ( c3y == ful_o ) { - u3l_log("mars: preparing for full replay\r\n"); - u3m_init((size_t)1 << u3_Host.ops_u.lom_y); - u3e_live(u3m_pier(u3_Host.dir_c)); - u3e_yolo(); - u3m_pave(c3y); - u3j_boot(c3y); - u3A->eve_d = 0; - } - else { - u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); - } - - u3C.slog_f = _cw_play_slog; - - { - u3_mars mar_u = { - .log_u = log_u, - .dir_c = u3_Host.dir_c, - .sen_d = u3A->eve_d, - .dun_d = u3A->eve_d, - .mug_l = u3r_mug(u3A->roc) - }; - c3_d eve_d = 0; - c3_c* eve_c = u3_Host.ops_u.til_c; - - if ( u3_Host.ops_u.til_c ) { - if ( 1 != sscanf(eve_c, "%" PRIu64 "", &eve_d) ) { - fprintf(stderr, "mars: replay-to invalid: '%s'\r\n", eve_c); - } - } - - u3_mars_play(&mar_u, eve_d); - } - + u3e_save(); u3_disk_exit(log_u); u3m_stop(); } @@ -1943,8 +1733,7 @@ _cw_prep(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, + { "loom", required_argument, NULL, c3__loom }, { NULL, 0, NULL, 0 } }; @@ -1952,11 +1741,6 @@ _cw_prep(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { - case 6: { // no-demand - u3_Host.ops_u.map = c3n; - u3C.wag_w |= u3o_no_demand; - } break; - case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -2120,8 +1904,7 @@ _cw_vile(c3_i argc, c3_c* argv[]) c3_w arg_w; static struct option lop_u[] = { - { "loom", required_argument, NULL, c3__loom }, - { "no-demand", no_argument, NULL, 6 }, + { "loom", required_argument, NULL, c3__loom }, { NULL, 0, NULL, 0 } }; @@ -2129,11 +1912,6 @@ _cw_vile(c3_i argc, c3_c* argv[]) while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { switch ( ch_i ) { - case 6: { // no-demand - u3_Host.ops_u.map = c3n; - u3C.wag_w |= u3o_no_demand; - } break; - case c3__loom: { c3_w lom_w; c3_o res_o = _main_readw(optarg, u3a_bits + 3, &lom_w); @@ -2221,13 +1999,11 @@ _cw_utils(c3_i argc, c3_c* argv[]) // $@ ~ :: usage // $% [%cram dir=@t] :: jam state // [%dock dir=@t] :: copy binary - // [%eval ~] :: eval hoon // [?(%grab %mass) dir=@t] :: gc // [%info dir=@t] :: print // [%meld dir=@t] :: deduplicate // [?(%next %upgrade) dir=@t] :: upgrade // [%pack dir=@t] :: defragment - // [%play dir=@t] :: recompute // [%prep dir=@t] :: prep upgrade // [%queu dir=@t eve=@ud] :: cue state // [?(%vere %fetch-vere) dir=@t] :: download vere @@ -2268,7 +2044,6 @@ _cw_utils(c3_i argc, c3_c* argv[]) case c3__meld: _cw_meld(argc, argv); return 1; case c3__next: _cw_next(argc, argv); return 2; // continue on case c3__pack: _cw_pack(argc, argv); return 1; - case c3__play: _cw_play(argc, argv); return 1; case c3__prep: _cw_prep(argc, argv); return 2; // continue on case c3__queu: _cw_queu(argc, argv); return 1; case c3__vere: _cw_vere(argc, argv); return 1; @@ -2406,12 +2181,6 @@ main(c3_i argc, u3C.wag_w |= u3o_debug_ram; } - /* Set no-demand flag. - */ - if ( !_(u3_Host.ops_u.map) ) { - u3C.wag_w |= u3o_no_demand; - } - /* Set profile flag. */ if ( _(u3_Host.ops_u.pro) ) { diff --git a/pkg/urbit/daemon/whereami.c b/pkg/urbit/daemon/whereami.c index 5e31a2490..290005766 100644 --- a/pkg/urbit/daemon/whereami.c +++ b/pkg/urbit/daemon/whereami.c @@ -1,6 +1,4 @@ -// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses -// without any warranty. -// by Gregory Pakosz (@gpakosz) +// (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz) // https://github.com/gpakosz/whereami // in case you want to #include "whereami.c" in a larger compilation unit @@ -12,15 +10,6 @@ extern "C" { #endif -#if defined(__linux__) || defined(__CYGWIN__) -#undef _DEFAULT_SOURCE -#define _DEFAULT_SOURCE -#elif defined(__APPLE__) -#undef _DARWIN_C_SOURCE -#define _DARWIN_C_SOURCE -#define _DARWIN_BETTER_REALPATH -#endif - #if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC) #include #endif @@ -57,9 +46,7 @@ extern "C" { #if defined(_WIN32) -#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN -#endif #if defined(_MSC_VER) #pragma warning(push, 3) #endif @@ -68,7 +55,6 @@ extern "C" { #if defined(_MSC_VER) #pragma warning(pop) #endif -#include static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length) { @@ -76,9 +62,8 @@ static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, i wchar_t buffer2[MAX_PATH]; wchar_t* path = NULL; int length = -1; - bool ok; - for (ok = false; !ok; ok = true) + for (;;) { DWORD size; int length_, length__; @@ -134,12 +119,14 @@ static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, i } length = length__; + + break; } if (path != buffer1) WAI_FREE(path); - return ok ? length : -1; + return length; } WAI_NOINLINE WAI_FUNCSPEC @@ -169,7 +156,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) return length; } -#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE) +#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) #include #include @@ -183,7 +170,6 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) #define __STDC_FORMAT_MACROS #endif #include -#include #if !defined(WAI_PROC_SELF_EXE) #if defined(__sun) @@ -199,9 +185,8 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) char buffer[PATH_MAX]; char* resolved = NULL; int length = -1; - bool ok; - for (ok = false; !ok; ok = true) + for (;;) { resolved = realpath(WAI_PROC_SELF_EXE, buffer); if (!resolved) @@ -226,9 +211,11 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) } } } + + break; } - return ok ? length : -1; + return length; } #if !defined(WAI_PROC_SELF_MAPS_RETRY) @@ -248,7 +235,6 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) #include #include #endif -#include WAI_NOINLINE WAI_FUNCSPEC int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) @@ -295,24 +281,15 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) &&buffer[length - 4] == '.') { int fd = open(path, O_RDONLY); - if (fd == -1) - { - length = -1; // retry - break; - } + char* begin; + char* p; - char* begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0); - if (begin == MAP_FAILED) - { - close(fd); - length = -1; // retry - break; - } + begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0); + p = begin + offset; - char* p = begin + offset - 30; // minimum size of local file header while (p >= begin) // scan backwards { - if (*((uint32_t*)p) == 0x04034b50UL) // local file header signature found + if (*((uint32_t*)p) == 0x04034b50UL) // local file header found { uint16_t length_ = *((uint16_t*)(p + 26)); @@ -326,7 +303,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) break; } - --p; + p -= 4; } munmap(begin, offset); @@ -364,17 +341,20 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) break; } + if (maps) + fclose(maps); + return length; } #elif defined(__APPLE__) +#define _DARWIN_BETTER_REALPATH #include #include #include #include #include -#include WAI_FUNCSPEC int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) @@ -384,9 +364,8 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) char* path = buffer1; char* resolved = NULL; int length = -1; - bool ok; - for (ok = false; !ok; ok = true) + for (;;) { uint32_t size = (uint32_t)sizeof(buffer1); if (_NSGetExecutablePath(path, &size) == -1) @@ -419,12 +398,14 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) } } } + + break; } if (path != buffer1) WAI_FREE(path); - return ok ? length : -1; + return length; } WAI_NOINLINE WAI_FUNCSPEC @@ -478,7 +459,6 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) #include #include #include -#include #if !defined(WAI_PROC_SELF_EXE) #define WAI_PROC_SELF_EXE "/proc/self/exefile" @@ -492,9 +472,8 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) char* resolved = NULL; FILE* self_exe = NULL; int length = -1; - bool ok; - for (ok = false; !ok; ok = true) + for (;;) { self_exe = fopen(WAI_PROC_SELF_EXE, "r"); if (!self_exe) @@ -526,11 +505,13 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) } } } + + break; } fclose(self_exe); - return ok ? length : -1; + return length; } WAI_FUNCSPEC @@ -578,7 +559,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) } #elif defined(__DragonFly__) || defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) + defined(__FreeBSD_kernel__) || defined(__NetBSD__) #include #include @@ -586,116 +567,6 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) #include #include #include -#include - -#if defined(__OpenBSD__) - -#include - -WAI_FUNCSPEC -int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) -{ - char buffer1[4096]; - char buffer2[PATH_MAX]; - char buffer3[PATH_MAX]; - char** argv = (char**)buffer1; - char* resolved = NULL; - int length = -1; - bool ok; - - for (ok = false; !ok; ok = true) - { - int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV }; - size_t size; - - if (sysctl(mib, 4, NULL, &size, NULL, 0) != 0) - break; - - if (size > sizeof(buffer1)) - { - argv = (char**)WAI_MALLOC(size); - if (!argv) - break; - } - - if (sysctl(mib, 4, argv, &size, NULL, 0) != 0) - break; - - if (strchr(argv[0], '/')) - { - resolved = realpath(argv[0], buffer2); - if (!resolved) - break; - } - else - { - const char* PATH = getenv("PATH"); - if (!PATH) - break; - - size_t argv0_length = strlen(argv[0]); - - const char* begin = PATH; - while (1) - { - const char* separator = strchr(begin, ':'); - const char* end = separator ? separator : begin + strlen(begin); - - if (end - begin > 0) - { - if (*(end -1) == '/') - --end; - - if (((end - begin) + 1 + argv0_length + 1) <= sizeof(buffer2)) - { - memcpy(buffer2, begin, end - begin); - buffer2[end - begin] = '/'; - memcpy(buffer2 + (end - begin) + 1, argv[0], argv0_length + 1); - - resolved = realpath(buffer2, buffer3); - if (resolved) - break; - } - } - - if (!separator) - break; - - begin = ++separator; - } - - if (!resolved) - break; - } - - length = (int)strlen(resolved); - if (length <= capacity) - { - memcpy(out, resolved, length); - - if (dirname_length) - { - int i; - - for (i = length - 1; i >= 0; --i) - { - if (out[i] == '/') - { - *dirname_length = i; - break; - } - } - } - } - } - - if (argv != (char**)buffer1) - WAI_FREE(argv); - - return ok ? length : -1; -} - -#else WAI_FUNCSPEC int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) @@ -705,18 +576,13 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) char* path = buffer1; char* resolved = NULL; int length = -1; - bool ok; - for (ok = false; !ok; ok = true) + for (;;) { -#if defined(__NetBSD__) - int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; -#else int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; -#endif size_t size = sizeof(buffer1); - if (sysctl(mib, 4, path, &size, NULL, 0) != 0) + if (sysctl(mib, (u_int)(sizeof(mib) / sizeof(mib[0])), path, &size, NULL, 0) != 0) break; resolved = realpath(path, buffer2); @@ -742,12 +608,15 @@ int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) } } } + + break; } - return ok ? length : -1; -} + if (path != buffer1) + WAI_FREE(path); -#endif + return length; +} WAI_NOINLINE WAI_FUNCSPEC int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) @@ -801,4 +670,4 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/pkg/urbit/daemon/whereami.h b/pkg/urbit/daemon/whereami.h index d5edffb8c..6c81af818 100644 --- a/pkg/urbit/daemon/whereami.h +++ b/pkg/urbit/daemon/whereami.h @@ -1,6 +1,4 @@ -// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses -// without any warranty. -// by Gregory Pakosz (@gpakosz) +// (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz) // https://github.com/gpakosz/whereami #ifndef WHEREAMI_H @@ -64,4 +62,4 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length); } #endif -#endif // #ifndef WHEREAMI_H \ No newline at end of file +#endif // #ifndef WHEREAMI_H diff --git a/pkg/urbit/include/all.h b/pkg/urbit/include/all.h index 402b88583..ae647d865 100644 --- a/pkg/urbit/include/all.h +++ b/pkg/urbit/include/all.h @@ -11,6 +11,7 @@ # include "noun/aliases.h" // general u3 # include "noun/allocate.h" // u3a: allocation +# include "noun/events.h" // u3e: persistence # include "noun/hashtable.h" // u3h: hashtables # include "noun/imprison.h" // u3i: noun construction # include "noun/jets.h" // u3j: jet control diff --git a/pkg/urbit/include/c/defs.h b/pkg/urbit/include/c/defs.h index a667b85a2..b649a8866 100644 --- a/pkg/urbit/include/c/defs.h +++ b/pkg/urbit/include/c/defs.h @@ -1,11 +1,6 @@ #ifndef C3_DEFS_H #define C3_DEFS_H -#include "c/portable.h" -#include "c/types.h" - -#include - /** Loobeans - inverse booleans to match nock. **/ # define c3y 0 @@ -162,28 +157,4 @@ # define c3_fopen(a, b) ({ \ fopen(a, b);}) - /** i/o wrappers - *** - *** these handle partial success and retry ephemeral errors - *** up to hardcoded max try count, either reading/writing fully - *** (up to EOF on read) or returning on error. - *** - *** a wrapper for read() is not provided, as file cursor position - *** is undefined on error. use pread() or loop yourself. - **/ - /* c3_pread(): full positioned read(), up to eof, retrying errors. - */ - ssize_t - c3_pread(c3_i fid_i, void* buf_v, size_t len_i, off_t off_i); - - /* c3_pwrite(): full positioned write(), retrying errors. - */ - ssize_t - c3_pwrite(c3_i fid_i, const void* buf_v, size_t len_i, off_t off_i); - - /* c3_write(): full write(), retrying errors. - */ - ssize_t - c3_write(c3_i fid_i, const void* buf_v, size_t len_i); - #endif /* ifndef C3_DEFS_H */ diff --git a/pkg/urbit/include/noun/allocate.h b/pkg/urbit/include/noun/allocate.h index 03b219c08..964de538b 100644 --- a/pkg/urbit/include/noun/allocate.h +++ b/pkg/urbit/include/noun/allocate.h @@ -645,11 +645,6 @@ void u3a_print_memory(FILE* fil_u, c3_c* cap_c, c3_w wor_w); - /* u3a_prof(): mark/measure/print memory profile. RETAIN. - */ - c3_w - u3a_prof(FILE* fil_u, c3_w den_w, u3_noun mas); - /* u3a_maid(): maybe print memory. */ c3_w diff --git a/pkg/urbit/include/noun/events.h b/pkg/urbit/include/noun/events.h index a8860f53d..c1b95cde8 100644 --- a/pkg/urbit/include/noun/events.h +++ b/pkg/urbit/include/noun/events.h @@ -39,22 +39,13 @@ /* u3e_pool: entire memory system. */ typedef struct _u3e_pool { - c3_c* dir_c; // checkpoint dir + c3_c* dir_c; // path to c3_w dit_w[u3a_pages >> 5]; // touched since last save c3_w pag_w; // number of pages (<= u3a_pages) - c3_w gar_w; // guard page u3e_image nor_u; // north segment u3e_image sou_u; // south segment } u3e_pool; - /* u3e_flaw: loom fault result. - */ - typedef enum { - u3e_flaw_sham = 0, // bogus state - u3e_flaw_base = 1, // vm fail (mprotect) - u3e_flaw_meme = 2, // bail:meme - u3e_flaw_good = 3 // handled - } u3e_flaw; /** Globals. **/ @@ -69,26 +60,31 @@ /** Functions. **/ - /* u3e_fault(): handle a memory fault. + /* u3e_fault(): handle a memory event with libsigsegv protocol. */ - u3e_flaw - u3e_fault(u3_post low_p, u3_post hig_p, u3_post off_p); + c3_i + u3e_fault(void* adr_v, c3_i ser_i); - /* u3e_save(): update the checkpoint. + /* u3e_save(): */ void - u3e_save(u3_post low_p, u3_post hig_p); + u3e_save(void); /* u3e_live(): start the persistence system. Return c3y if no image. */ c3_o - u3e_live(c3_c* dir_c); + u3e_live(c3_o nuu_o, c3_c* dir_c); /* u3e_yolo(): disable dirty page tracking, read/write whole loom. */ c3_o u3e_yolo(void); + /* u3e_foul(): dirty all the pages of the loom. + */ + void + u3e_foul(void); + /* u3e_init(): initialize guard page tracking. */ void diff --git a/pkg/urbit/include/noun/manage.h b/pkg/urbit/include/noun/manage.h index 0ff5b504f..72e29f533 100644 --- a/pkg/urbit/include/noun/manage.h +++ b/pkg/urbit/include/noun/manage.h @@ -8,11 +8,6 @@ c3_d u3m_boot(c3_c* dir_c, size_t len_i); - /* u3m_pier(): make a pier. - */ - c3_c* - u3m_pier(c3_c* dir_c); - /* u3m_boot_lite(): start without checkpointing. */ c3_d @@ -39,21 +34,6 @@ c3_i u3m_bail(c3_m how_m) __attribute__((noreturn)); - /* u3m_fault(): handle a memory event with libsigsegv protocol. - */ - c3_i - u3m_fault(void* adr_v, c3_i ser_i); - - /* u3m_save(): update the checkpoint. - */ - void - u3m_save(void); - - /* u3m_ward(): tend the guard page. - */ - void - u3m_ward(void); - /* u3m_init(): start the environment. */ void @@ -137,7 +117,7 @@ /* u3m_water(): produce high and low watermarks. Asserts u3R == u3H. */ void - u3m_water(u3_post* low_p, u3_post* hig_p); + u3m_water(c3_w *low_w, c3_w *hig_w); /* u3m_pretty(): dumb prettyprint to string. RETAIN. */ diff --git a/pkg/urbit/include/noun/options.h b/pkg/urbit/include/noun/options.h index 26d081f4e..c1fd58c40 100644 --- a/pkg/urbit/include/noun/options.h +++ b/pkg/urbit/include/noun/options.h @@ -22,17 +22,15 @@ ** _check flags are set inside u3 and heard outside it. */ enum u3o_flag { // execution flags - u3o_debug_ram = 1 << 0, // debug: gc - u3o_debug_cpu = 1 << 1, // debug: profile - u3o_check_corrupt = 1 << 2, // check: gc memory - u3o_check_fatal = 1 << 3, // check: unrecoverable - u3o_verbose = 1 << 4, // be remarkably wordy - u3o_dryrun = 1 << 5, // don't touch checkpoint - u3o_quiet = 1 << 6, // disable ~& - u3o_hashless = 1 << 7, // disable hashboard - u3o_trace = 1 << 8, // enables trace dumping - u3o_auto_meld = 1 << 9, // enables meld under pressure - u3o_no_demand = 1 << 10 // disables demand paging + u3o_debug_ram = 0x1, // debug: gc + u3o_debug_cpu = 0x2, // debug: profile + u3o_check_corrupt = 0x4, // check: gc memory + u3o_check_fatal = 0x8, // check: unrecoverable + u3o_verbose = 0x10, // be remarkably wordy + u3o_dryrun = 0x20, // don't touch checkpoint + u3o_quiet = 0x40, // disable ~& + u3o_hashless = 0x80, // disable hashboard + u3o_trace = 0x100 // enables trace dumping }; /** Globals. diff --git a/pkg/urbit/include/noun/urth.h b/pkg/urbit/include/noun/urth.h index 2c6617f4b..3fe29792e 100644 --- a/pkg/urbit/include/noun/urth.h +++ b/pkg/urbit/include/noun/urth.h @@ -5,7 +5,7 @@ **/ /* u3u_meld(): globally deduplicate memory. */ - c3_w + void u3u_meld(void); /* u3u_cram(): globably deduplicate memory, and write a rock to disk. diff --git a/pkg/urbit/include/noun/vortex.h b/pkg/urbit/include/noun/vortex.h index ed24e691e..92320620a 100644 --- a/pkg/urbit/include/noun/vortex.h +++ b/pkg/urbit/include/noun/vortex.h @@ -87,11 +87,6 @@ u3_noun u3v_poke(u3_noun ovo); - /* u3v_poke_sure(): inject an event, saving new state if successful. - */ - c3_o - u3v_poke_sure(c3_w mil_w, u3_noun eve, u3_noun* pro); - /* u3v_tank(): dump single tank. */ void diff --git a/pkg/urbit/include/vere/db/lmdb.h b/pkg/urbit/include/vere/db/lmdb.h index a3ca6a414..573a68fa4 100644 --- a/pkg/urbit/include/vere/db/lmdb.h +++ b/pkg/urbit/include/vere/db/lmdb.h @@ -6,17 +6,6 @@ /* lmdb api wrapper */ - /* u3_lmdb_iter: event iterator - */ - typedef struct _u3_lmdb_walk { - MDB_txn* txn_u; // transaction handle - MDB_dbi mdb_u; // db handle - MDB_cursor* cur_u; // db cursor - c3_o red_o; // have we read from this yet? - c3_d nex_d; // next event number - c3_d las_d; // final event number, inclusive - } u3_lmdb_walk; - /* u3_lmdb_init(): open lmdb at [pax_c], mmap up to [siz_i]. */ MDB_env* @@ -72,22 +61,4 @@ size_t val_i, void* val_p); - /* u3_lmdb_walk_init(): initialize db iterator. - */ - c3_o - u3_lmdb_walk_init(MDB_env* env_u, - u3_lmdb_walk* itr_u, - c3_d nex_d, - c3_d las_d); - - /* u3_lmdb_walk_next(): synchronously read next event from iterator. - */ - c3_o - u3_lmdb_walk_next(u3_lmdb_walk* itr_u, size_t* len_i, void** buf_v); - - /* u3_lmdb_walk_done(): close iterator. - */ - void - u3_lmdb_walk_done(u3_lmdb_walk* itr_u); - #endif /* ifndef U3_VERE_DB_LMDB_H */ diff --git a/pkg/urbit/include/vere/mars.h b/pkg/urbit/include/vere/mars.h deleted file mode 100644 index a445af0ac..000000000 --- a/pkg/urbit/include/vere/mars.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef U3_VERE_MARS_H -#define U3_VERE_MARS_H - - /** Data types. - **/ - /* u3_mars: the urbit state machine. - */ - typedef struct _u3_mars { - c3_d key_d[4]; // disk key - u3_disk* log_u; // event log - c3_c* dir_c; // execution directory (pier) - c3_d sen_d; // last event requested - c3_d dun_d; // last event processed - c3_l mug_l; // hash of state - } u3_mars; - - /* u3_mars_play(): replay logged events up to [eve_d]. - */ - void - u3_mars_play(u3_mars* mar_u, c3_d eve_d); - -#endif /* ifndef U3_VERE_MARS_H */ diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index ed718fd79..d02771343 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -313,7 +313,6 @@ c3_c* puf_c; // -Z, scry result format c3_o con; // run conn c3_o doc; // dock binary in pier - c3_o map; // --no-demand (reversed) } u3_opts; /* u3_host: entire host. @@ -563,10 +562,6 @@ u3_info put_u; // write queue } u3_disk; - /* u3_disk_walk: opaque event log iterator. - */ - typedef struct _u3_disk_walk u3_disk_walk; - /* u3_psat: pier state. */ typedef enum { @@ -947,23 +942,6 @@ u3_disk* u3_disk_init(c3_c* pax_c, u3_disk_cb cb_u); - /* u3_disk_etch(): serialize an event for persistence. RETAIN [eve] - */ - size_t - u3_disk_etch(u3_disk* log_u, - u3_noun eve, - c3_l mug_l, - c3_y** out_y); - - /* u3_disk_sift(): parse a persisted event buffer. - */ - c3_o - u3_disk_sift(u3_disk* log_u, - size_t len_i, - c3_y* dat_y, - c3_l* mug_l, - u3_noun* job); - /* u3_disk_info(): status info as $mass. */ u3_noun @@ -1015,33 +993,6 @@ void u3_disk_plan(u3_disk* log_u, u3_fact* tac_u); - /* u3_disk_read_list(): synchronously read a cons list of events. - */ - u3_weak - u3_disk_read_list(u3_disk* log_u, c3_d eve_d, c3_d len_d, c3_l* mug_l); - - /* u3_disk_walk_init(): init iterator. - */ - u3_disk_walk* - u3_disk_walk_init(u3_disk* log_u, - c3_d eve_d, - c3_d len_d); - - /* u3_disk_walk_live(): check if live. - */ - c3_o - u3_disk_walk_live(u3_disk_walk* wok_u); - - /* u3_disk_walk_live(): get next fact. - */ - c3_o - u3_disk_walk_step(u3_disk_walk* wok_u, u3_fact* tac_u); - - /* u3_disk_walk_done(): close iterator. - */ - void - u3_disk_walk_done(u3_disk_walk* wok_u); - /* u3_lord_init(): start serf. */ u3_lord* @@ -1510,6 +1461,11 @@ void u3_daemon_init(); + /* u3_write_fd(): retry interrupts, continue partial writes, assert errors. + */ + void + u3_write_fd(c3_i fid_i, const void* buf_v, size_t len_i); + c3_w u3_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); diff --git a/pkg/urbit/noun/allocate.c b/pkg/urbit/noun/allocate.c index 093c6fefb..f7ce67958 100644 --- a/pkg/urbit/noun/allocate.c +++ b/pkg/urbit/noun/allocate.c @@ -425,18 +425,8 @@ _ca_willoc(c3_w len_w, c3_w ald_w, c3_w alp_w) alp_w = (alp_w + c3_wiseof(u3a_box)) % ald_w; - /* XX: this logic is totally bizarre, but preserve it. - ** - ** This means we use the next size bigger instead of the "correct" - ** size. For example, a 20 word allocation will be freed into free - ** list 2 but will be allocated from free list 3. - ** - ** This is important to preserve because the sequential search may be - ** very slow. On a real-world task involving many compilations, - ** removing this line made this function appear in ~80% of samples. - ** - ** For reference, this was added in cgyarvin/urbit ffed9e748d8f6c. - */ + // XX: this logic is totally bizarre, but preserve it. + // if ( (sel_w != 0) && (sel_w != u3a_fbox_no - 1) ) { sel_w += 1; } @@ -1627,7 +1617,7 @@ u3a_mark_ptr(void* ptr_v) if ( 0 == box_u->eus_w ) { siz_w = box_u->siz_w; } - else if ( 0xffffffff == box_u->eus_w ) { // see u3a_prof() + else if ( 0xffffffff == box_u->eus_w ) { // see _raft_prof() siz_w = 0xffffffff; box_u->eus_w = 0; } @@ -1645,7 +1635,7 @@ u3a_mark_ptr(void* ptr_v) else { c3_assert(use_ws != 0); - if ( 0x80000000 == (c3_w)use_ws ) { // see u3a_prof() + if ( 0x80000000 == (c3_w)use_ws ) { // see _raft_prof() use_ws = -1; siz_w = 0xffffffff; } @@ -1916,7 +1906,7 @@ u3a_print_memory(FILE* fil_u, c3_c* cap_c, c3_w wor_w) if ( byt_w ) { if ( gib_w ) { fprintf(fil_u, "%s: GB/%d.%03d.%03d.%03d\r\n", - cap_c, gib_w, mib_w, kib_w, bib_w); + cap_c, gib_w, mib_w, kib_w, bib_w); } else if ( mib_w ) { fprintf(fil_u, "%s: MB/%d.%03d.%03d\r\n", cap_c, mib_w, kib_w, bib_w); @@ -1941,122 +1931,6 @@ u3a_maid(FILE* fil_u, c3_c* cap_c, c3_w wor_w) return wor_w; } -/* _ca_print_memory(): un-captioned u3a_print_memory(). -*/ -static void -_ca_print_memory(FILE* fil_u, c3_w wor_w) -{ - c3_w byt_w = (wor_w * 4); - c3_w gib_w = (byt_w / 1000000000); - c3_w mib_w = (byt_w % 1000000000) / 1000000; - c3_w kib_w = (byt_w % 1000000) / 1000; - c3_w bib_w = (byt_w % 1000); - - if ( gib_w ) { - fprintf(fil_u, "GB/%d.%03d.%03d.%03d\r\n", - gib_w, mib_w, kib_w, bib_w); - } - else if ( mib_w ) { - fprintf(fil_u, "MB/%d.%03d.%03d\r\n", mib_w, kib_w, bib_w); - } - else if ( kib_w ) { - fprintf(fil_u, "KB/%d.%03d\r\n", kib_w, bib_w); - } - else { - fprintf(fil_u, "B/%d\r\n", bib_w); - } -} - -/* u3a_prof(): mark/measure/print memory profile. RETAIN. -*/ -c3_w -u3a_prof(FILE* fil_u, c3_w den_w, u3_noun mas) -{ - c3_w tot_w = 0; - u3_noun h_mas, t_mas; - - if ( c3n == u3r_cell(mas, &h_mas, &t_mas) ) { - fprintf(fil_u, "%.*smistyped mass\r\n", den_w, ""); - return tot_w; - } - else if ( _(u3du(h_mas)) ) { - fprintf(fil_u, "%.*smistyped mass head\r\n", den_w, ""); - { - c3_c* lab_c = u3m_pretty(h_mas); - fprintf(fil_u, "h_mas: %s", lab_c); - c3_free(lab_c); - } - return tot_w; - } - else { - { - c3_c* lab_c = u3m_pretty(h_mas); - fprintf(fil_u, "%*s%s: ", den_w, "", lab_c); - c3_free(lab_c); - } - - u3_noun it_mas, tt_mas; - - if ( c3n == u3r_cell(t_mas, &it_mas, &tt_mas) ) { - fprintf(fil_u, "%*smistyped mass tail\r\n", den_w, ""); - return tot_w; - } - else if ( c3y == it_mas ) { - tot_w += u3a_mark_noun(tt_mas); - _ca_print_memory(fil_u, tot_w); - -#if 1 - /* The basic issue here is that tt_mas is included in .sac - * (the whole profile), so they can't both be roots in the - * normal sense. When we mark .sac later on, we want tt_mas - * to appear unmarked, but its children should be already - * marked. - * - * see u3a_mark_ptr(). - */ - if ( _(u3a_is_dog(tt_mas)) ) { - u3a_box* box_u = u3a_botox(u3a_to_ptr(tt_mas)); -#ifdef U3_MEMORY_DEBUG - if ( 1 == box_u->eus_w ) { - box_u->eus_w = 0xffffffff; - } - else { - box_u->eus_w -= 1; - } -#else - if ( -1 == (c3_w)box_u->use_w ) { - box_u->use_w = 0x80000000; - } - else { - box_u->use_w += 1; - } -#endif - } -#endif - - return tot_w; - } - else if ( c3n == it_mas ) { - fprintf(fil_u, "\r\n"); - - while ( _(u3du(tt_mas)) ) { - tot_w += u3a_prof(fil_u, den_w+2, u3h(tt_mas)); - tt_mas = u3t(tt_mas); - } - - fprintf(fil_u, "%*s--", den_w, ""); - _ca_print_memory(fil_u, tot_w); - - return tot_w; - - } - else { - fprintf(fil_u, "%*smistyped (strange) mass tail\r\n", den_w, ""); - return tot_w; - } - } -} - /* u3a_mark_road(): mark ad-hoc persistent road structures. */ c3_w diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index 7b1f78b21..ceec1bd41 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -7,7 +7,7 @@ //! - page: 16KB chunk of the loom. //! - north segment (u3e_image, north.bin): low contiguous loom pages, //! (in practice, the home road heap). indexed from low to high: -//! in-order on disk. in a file-backed mapping by default. +//! in-order on disk. //! - south segment (u3e_image, south.bin): high contiguous loom pages, //! (in practice, the home road stack). indexed from high to low: //! reversed on disk. @@ -20,8 +20,8 @@ //! - with the loom already mapped, all pages are marked dirty in a bitmap. //! - if snapshot is missing or partial, empty segments are created. //! - if a patch is present, it's applied (crash recovery). -//! - snapshot segments are mapped or copied onto the loom; -//! all included pages are marked clean and protected (read-only). +//! - snapshot segments are copied onto the loom; all included pages +//! are marked clean and protected (read-only). //! //! #### page faults (u3e_fault()) //! @@ -47,60 +47,43 @@ //! contiguous free space). //! - patch pages are written to memory.bin, metadata to control.bin. //! - the patch is applied to the snapshot segments, in-place. -//! - memory protections (and file-backed mappings) are re-established. //! - patch files are deleted. //! -//! ### invariants -//! -//! definitions: -//! - a clean page is PROT_READ and 0 in the bitmap -//! - a dirty page is (PROT_READ|PROT_WRITE) and 1 in the bitmap -//! - the guard page is PROT_NONE and 1 in the bitmap -//! -//! assumptions: -//! - all memory access patterns are outside-in, a page at a time -//! - ad-hoc exceptions are supported by calling u3e_ward() -//! -//! - there is a single guard page, between the segments -//! - dirty pages only become clean by being: -//! - loaded from a snapshot during initialization -//! - present in a snapshot after save -//! - clean pages only become dirty by being: -//! - modified (and caught by the fault handler) -//! - orphaned due to segment truncation (explicitly dirtied) -//! - at points of quiescence (initialization, after save) -//! - all pages of the north and south segments are clean -//! - all other pages are dirty -//! //! ### limitations //! //! - loom page size is fixed (16 KB), and must be a multiple of the -//! system page size. -//! - update atomicity is crucial: -//! - patch application must either completely succeed or -//! leave on-disk segments (memory image) intact. -//! - unapplied patches can be discarded (triggering event replay), -//! but once patch application begins it must succeed. -//! - may require integration into the overall signal-handling regime. -//! - many errors are handled with assertions. +//! system page size. (can the size vary at runtime give south.bin's +//! reversed order? alternately, if system page size > ours, the fault +//! handler could dirty N pages at a time.) +//! - update atomicity is suspect: patch application must either +//! completely succeed or leave on-disk segments intact. unapplied +//! patches can be discarded (triggering event replay), but once +//! patch application begins it must succeed (can fail if disk is full). +//! may require integration into the overall signal-handling regime. +//! - any errors are handled with assertions; failed/partial writes are not +//! retried. //! //! ### enhancements //! //! - use platform specific page fault mechanism (mach rpc, userfaultfd, &c). -//! - parallelism (conflicts with demand paging) +//! - implement demand paging / heuristic page-out. +//! - add a guard page in the middle of the loom to reactively handle stack overflow. +//! - parallelism //! #include "all.h" -#include "noun/events.h" #include #include #include +// Base loom offset of the guard page. +static u3p(c3_w) gar_pag_p; + //! Urbit page size in 4-byte words. -#define pag_wiz_i ((size_t)1 << u3a_page) +static const size_t pag_wiz_i = 1 << u3a_page; //! Urbit page size in bytes. -#define pag_siz_i ((size_t)1 << (u3a_page + 2)) +static const size_t pag_siz_i = sizeof(c3_w) * pag_wiz_i; #ifdef U3_SNAPSHOT_VALIDATION /* Image check. @@ -117,7 +100,7 @@ static c3_w _ce_check_page(c3_w pag_w) { c3_w* mem_w = u3_Loom + (pag_w << u3a_page); - c3_w mug_w = u3r_mug_words(mem_w, pag_wiz_i); + c3_w mug_w = u3r_mug_words(mem_w, (1 << u3a_page)); return mug_w; } @@ -135,8 +118,8 @@ u3e_check(c3_c* cap_c) u3m_water(&nwr_w, &swu_w); - nor_w = (nwr_w + (pag_wiz_i - 1)) >> u3a_page; - sou_w = (swu_w + (pag_wiz_i - 1)) >> u3a_page; + nor_w = (nwr_w + ((1 << u3a_page) - 1)) >> u3a_page; + sou_w = (swu_w + ((1 << u3a_page) - 1)) >> u3a_page; } /* Count dirty pages. @@ -162,115 +145,156 @@ u3e_check(c3_c* cap_c) u3l_log("%s: sum %x (%x, %x)\r\n", cap_c, sum_w, nor_w, sou_w); } } + +/* _ce_maplloc(): crude off-loom allocator. +*/ +static void* +_ce_maplloc(c3_w len_w) +{ + void* map_v; + + map_v = mmap(0, + len_w, + (PROT_READ | PROT_WRITE), + (MAP_ANON | MAP_PRIVATE), + -1, 0); + + if ( -1 == (c3_ps)map_v ) { + c3_assert(0); + } + else { + c3_w* map_w = map_v; + + map_w[0] = len_w; + + return map_w + 1; + } +} + +/* _ce_mapfree(): crude off-loom allocator. +*/ +static void +_ce_mapfree(void* map_v) +{ + c3_w* map_w = map_v; + c3_i res_i; + + map_w -= 1; + res_i = munmap(map_w, map_w[0]); + + c3_assert(0 == res_i); +} #endif -/* _ce_flaw_protect(): protect page after fault. -*/ -static inline c3_i -_ce_flaw_protect(c3_w pag_w) -{ - if ( 0 != mprotect((void *)(u3_Loom + (pag_w << u3a_page)), - pag_siz_i, - (PROT_READ | PROT_WRITE)) ) - { - fprintf(stderr, "loom: fault mprotect (%u): %s\r\n", - pag_w, strerror(errno)); - return 1; - } - - return 0; -} - #ifdef U3_GUARD_PAGE -/* _ce_ward_protect(): protect the guard page. -*/ -static inline c3_i -_ce_ward_protect(void) +//! Place a guard page at the (approximate) middle of the free space between +//! the heap and stack of the current road, bailing if memory has been +//! exhausted. +static c3_i +_ce_center_guard_page(void) { - if ( 0 != mprotect((void *)(u3_Loom + (u3P.gar_w << u3a_page)), - pag_siz_i, - PROT_NONE) ) - { - fprintf(stderr, "loom: failed to protect guard page (%u): %s\r\n", - u3P.gar_w, strerror(errno)); - return 1; + u3p(c3_w) bot_p, top_p; + if ( !u3R ) { + top_p = u3a_outa(u3_Loom + u3C.wor_i); + bot_p = u3a_outa(u3_Loom); + } + else if ( c3y == u3a_is_north(u3R) ) { + top_p = c3_rod(u3R->cap_p, pag_wiz_i); + bot_p = c3_rop(u3R->hat_p, pag_wiz_i); + } + else { + top_p = c3_rod(u3R->hat_p, pag_wiz_i); + bot_p = c3_rop(u3R->cap_p, pag_wiz_i); } + if ( top_p < bot_p + pag_wiz_i ) { + fprintf(stderr, + "loom: not enough memory to recenter the guard page\r\n"); + goto bail; + } + const u3p(c3_w) old_gar_p = gar_pag_p; + const c3_w mid_p = (top_p - bot_p) / 2; + gar_pag_p = bot_p + c3_rod(mid_p, pag_wiz_i); + if ( old_gar_p == gar_pag_p ) { + fprintf(stderr, + "loom: can't move the guard page to the same location" + " (base address %p)\r\n", + u3a_into(gar_pag_p)); + goto bail; + } + + if ( -1 == mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { + fprintf(stderr, + "loom: failed to protect the guard page " + "(base address %p): %s\r\n", + u3a_into(gar_pag_p), + strerror(errno)); + goto fail; + } + + return 1; + +bail: + u3m_signal(c3__meme); +fail: return 0; } - -/* _ce_ward_post(): set the guard page. -*/ -static inline c3_i -_ce_ward_post(c3_w nop_w, c3_w sop_w) -{ - u3P.gar_w = nop_w + ((sop_w - nop_w) / 2); - return _ce_ward_protect(); -} - -/* _ce_ward_clip(): hit the guard page. -*/ -static inline u3e_flaw -_ce_ward_clip(c3_w nop_w, c3_w sop_w) -{ - c3_w old_w = u3P.gar_w; - - if ( !u3P.gar_w || ((nop_w < u3P.gar_w) && (sop_w > u3P.gar_w)) ) { - fprintf(stderr, "loom: ward bogus (>%u %u %u<)\r\n", - nop_w, u3P.gar_w, sop_w); - return u3e_flaw_sham; - } - - if ( sop_w <= (nop_w + 1) ) { - return u3e_flaw_meme; - } - - if ( _ce_ward_post(nop_w, sop_w) ) { - return u3e_flaw_base; - } - - c3_assert( old_w != u3P.gar_w ); - - return u3e_flaw_good; -} #endif /* ifdef U3_GUARD_PAGE */ -/* u3e_fault(): handle a memory fault. +/* u3e_fault(): handle a memory event with libsigsegv protocol. */ -u3e_flaw -u3e_fault(u3_post low_p, u3_post hig_p, u3_post off_p) +c3_i +u3e_fault(void* adr_v, c3_i ser_i) { - c3_w pag_w = off_p >> u3a_page; - c3_w blk_w = pag_w >> 5; - c3_w bit_w = pag_w & 31; + // Let the stack overflow handler run. + if ( 0 == ser_i ) { + return 0; + } + + // XX u3l_log avoid here, as it can + // cause problems when handling errors + + c3_w* adr_w = (c3_w*) adr_v; + + if ( (adr_w < u3_Loom) || (adr_w >= (u3_Loom + u3C.wor_i)) ) { + fprintf(stderr, "address %p out of loom!\r\n", adr_w); + fprintf(stderr, "loom: [%p : %p)\r\n", u3_Loom, u3_Loom + u3C.wor_i); + c3_assert(0); + return 0; + } + + u3p(c3_w) adr_p = u3a_outa(adr_w); + c3_w pag_w = adr_p >> u3a_page; + c3_w blk_w = (pag_w >> 5); + c3_w bit_w = (pag_w & 31); #ifdef U3_GUARD_PAGE - if ( pag_w == u3P.gar_w ) { - u3e_flaw fal_e = _ce_ward_clip(low_p >> u3a_page, hig_p >> u3a_page); - - if ( u3e_flaw_good != fal_e ) { - return fal_e; - } - - if ( !(u3P.dit_w[blk_w] & (1 << bit_w)) ) { - fprintf(stderr, "loom: strange guard (%d)\r\n", pag_w); - return u3e_flaw_sham; + // The fault happened in the guard page. + if ( gar_pag_p <= adr_p && adr_p < gar_pag_p + pag_wiz_i ) { + if ( 0 == _ce_center_guard_page() ) { + return 0; } } else -#endif - if ( u3P.dit_w[blk_w] & (1 << bit_w) ) { - fprintf(stderr, "loom: strange page (%d): %x\r\n", pag_w, off_p); - return u3e_flaw_sham; +#endif /* ifdef U3_GUARD_PAGE */ + if ( 0 != (u3P.dit_w[blk_w] & (1 << bit_w)) ) { + fprintf(stderr, "strange page: %d, at %p, off %x\r\n", pag_w, adr_w, adr_p); + c3_assert(0); + return 0; } u3P.dit_w[blk_w] |= (1 << bit_w); - if ( _ce_flaw_protect(pag_w) ) { - return u3e_flaw_base; + if ( -1 == mprotect((void *)(u3_Loom + (pag_w << u3a_page)), + pag_siz_i, + (PROT_READ | PROT_WRITE)) ) + { + fprintf(stderr, "loom: fault mprotect: %s\r\n", strerror(errno)); + c3_assert(0); + return 0; } - return u3e_flaw_good; + return 1; } /* _ce_image_open(): open or create image. @@ -281,7 +305,16 @@ _ce_image_open(u3e_image* img_u) c3_i mod_i = O_RDWR | O_CREAT; c3_c ful_c[8193]; - snprintf(ful_c, 8192, "%s/%s.bin", u3P.dir_c, img_u->nam_c); + snprintf(ful_c, 8192, "%s", u3P.dir_c); + c3_mkdir(ful_c, 0700); + + snprintf(ful_c, 8192, "%s/.urb", u3P.dir_c); + c3_mkdir(ful_c, 0700); + + snprintf(ful_c, 8192, "%s/.urb/chk", u3P.dir_c); + c3_mkdir(ful_c, 0700); + + snprintf(ful_c, 8192, "%s/.urb/chk/%s.bin", u3P.dir_c, img_u->nam_c); if ( -1 == (img_u->fid_i = c3_open(ful_c, mod_i, 0666)) ) { fprintf(stderr, "loom: c3_open %s: %s\r\n", ful_c, strerror(errno)); return c3n; @@ -321,11 +354,17 @@ _ce_image_open(u3e_image* img_u) static void _ce_patch_write_control(u3_ce_patch* pat_u) { - c3_w len_w = sizeof(u3e_control) + - (pat_u->con_u->pgs_w * sizeof(u3e_line)); + ssize_t ret_i; + c3_w len_w = sizeof(u3e_control) + + (pat_u->con_u->pgs_w * sizeof(u3e_line)); - if ( 0 > c3_pwrite(pat_u->ctl_i, pat_u->con_u, len_w, 0) ) { - fprintf(stderr, "loom: patch write: %s\r\n", strerror(errno)); + if ( len_w != (ret_i = write(pat_u->ctl_i, pat_u->con_u, len_w)) ) { + if ( 0 < ret_i ) { + fprintf(stderr, "loom: patch ctl partial write: %zu\r\n", (size_t)ret_i); + } + else { + fprintf(stderr, "loom: patch ctl write: %s\r\n", strerror(errno)); + } c3_assert(0); } } @@ -349,9 +388,8 @@ _ce_patch_read_control(u3_ce_patch* pat_u) } pat_u->con_u = c3_malloc(len_w); - - if ( (len_w != c3_pread(pat_u->ctl_i, pat_u->con_u, len_w, 0)) - || (len_w != sizeof(u3e_control) + + if ( (len_w != read(pat_u->ctl_i, pat_u->con_u, len_w)) || + (len_w != sizeof(u3e_control) + (pat_u->con_u->pgs_w * sizeof(u3e_line))) ) { c3_free(pat_u->con_u); @@ -368,13 +406,19 @@ _ce_patch_create(u3_ce_patch* pat_u) { c3_c ful_c[8193]; - snprintf(ful_c, 8192, "%s/control.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s", u3P.dir_c); + c3_mkdir(ful_c, 0700); + + snprintf(ful_c, 8192, "%s/.urb", u3P.dir_c); + c3_mkdir(ful_c, 0700); + + snprintf(ful_c, 8192, "%s/.urb/chk/control.bin", u3P.dir_c); if ( -1 == (pat_u->ctl_i = c3_open(ful_c, O_RDWR | O_CREAT | O_EXCL, 0600)) ) { fprintf(stderr, "loom: patch c3_open control.bin: %s\r\n", strerror(errno)); c3_assert(0); } - snprintf(ful_c, 8192, "%s/memory.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s/.urb/chk/memory.bin", u3P.dir_c); if ( -1 == (pat_u->mem_i = c3_open(ful_c, O_RDWR | O_CREAT | O_EXCL, 0600)) ) { fprintf(stderr, "loom: patch c3_open memory.bin: %s\r\n", strerror(errno)); c3_assert(0); @@ -388,13 +432,13 @@ _ce_patch_delete(void) { c3_c ful_c[8193]; - snprintf(ful_c, 8192, "%s/control.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s/.urb/chk/control.bin", u3P.dir_c); if ( unlink(ful_c) ) { fprintf(stderr, "loom: failed to delete control.bin: %s\r\n", strerror(errno)); } - snprintf(ful_c, 8192, "%s/memory.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s/.urb/chk/memory.bin", u3P.dir_c); if ( unlink(ful_c) ) { fprintf(stderr, "loom: failed to remove memory.bin: %s\r\n", strerror(errno)); @@ -406,10 +450,8 @@ _ce_patch_delete(void) static c3_o _ce_patch_verify(u3_ce_patch* pat_u) { - c3_w i_w, pag_w, mug_w; - c3_w mem_w[pag_wiz_i]; - size_t off_i, siz_i = pag_siz_i; - ssize_t ret_i; + ssize_t ret_i; + c3_w i_w; if ( u3e_version != pat_u->con_u->ver_y ) { fprintf(stderr, "loom: patch version mismatch: have %u, need %u\r\n", @@ -419,11 +461,15 @@ _ce_patch_verify(u3_ce_patch* pat_u) } for ( i_w = 0; i_w < pat_u->con_u->pgs_w; i_w++ ) { - pag_w = pat_u->con_u->mem_u[i_w].pag_w; - mug_w = pat_u->con_u->mem_u[i_w].mug_w; - off_i = (size_t)i_w << (u3a_page + 2); + c3_w pag_w = pat_u->con_u->mem_u[i_w].pag_w; + c3_w mug_w = pat_u->con_u->mem_u[i_w].mug_w; + c3_w mem_w[1 << u3a_page]; - if ( siz_i != (ret_i = c3_pread(pat_u->mem_i, mem_w, siz_i, off_i)) ) { + if ( -1 == lseek(pat_u->mem_i, (i_w << (u3a_page + 2)), SEEK_SET) ) { + fprintf(stderr, "loom: patch seek: %s\r\n", strerror(errno)); + return c3n; + } + if ( pag_siz_i != (ret_i = read(pat_u->mem_i, mem_w, pag_siz_i)) ) { if ( 0 < ret_i ) { fprintf(stderr, "loom: patch partial read: %zu\r\n", (size_t)ret_i); } @@ -432,7 +478,6 @@ _ce_patch_verify(u3_ce_patch* pat_u) } return c3n; } - { c3_w nug_w = u3r_mug_words(mem_w, pag_wiz_i); @@ -471,12 +516,18 @@ _ce_patch_open(void) c3_c ful_c[8193]; c3_i ctl_i, mem_i; - snprintf(ful_c, 8192, "%s/control.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s", u3P.dir_c); + c3_mkdir(ful_c, 0700); + + snprintf(ful_c, 8192, "%s/.urb", u3P.dir_c); + c3_mkdir(ful_c, 0700); + + snprintf(ful_c, 8192, "%s/.urb/chk/control.bin", u3P.dir_c); if ( -1 == (ctl_i = c3_open(ful_c, O_RDWR)) ) { return 0; } - snprintf(ful_c, 8192, "%s/memory.bin", u3P.dir_c); + snprintf(ful_c, 8192, "%s/.urb/chk/memory.bin", u3P.dir_c); if ( -1 == (mem_i = c3_open(ful_c, O_RDWR)) ) { close(ctl_i); @@ -504,6 +555,32 @@ _ce_patch_open(void) return pat_u; } +/* _ce_patch_write_page(): write a page of patch memory. +*/ +static void +_ce_patch_write_page(u3_ce_patch* pat_u, + c3_w pgc_w, + c3_w* mem_w) +{ + ssize_t ret_i; + + if ( -1 == lseek(pat_u->mem_i, pgc_w * pag_siz_i, SEEK_SET) ) { + fprintf(stderr, "loom: patch page seek: %s\r\n", strerror(errno)); + c3_assert(0); + } + + if ( pag_siz_i != (ret_i = write(pat_u->mem_i, mem_w, pag_siz_i)) ) { + if ( 0 < ret_i ) { + fprintf(stderr, "loom: patch page partial write: %zu\r\n", + (size_t)ret_i); + } + else { + fprintf(stderr, "loom: patch page write: %s\r\n", strerror(errno)); + } + c3_assert(0); + } +} + /* _ce_patch_count_page(): count a page, producing new counter. */ static c3_w @@ -530,17 +607,25 @@ _ce_patch_save_page(u3_ce_patch* pat_u, c3_w bit_w = (pag_w & 31); if ( u3P.dit_w[blk_w] & (1 << bit_w) ) { - c3_w* mem_w = u3_Loom + (pag_w << u3a_page); - size_t off_i = (size_t)pgc_w << (u3a_page + 2); + c3_w* mem_w = u3_Loom + (pag_w << u3a_page); pat_u->con_u->mem_u[pgc_w].pag_w = pag_w; pat_u->con_u->mem_u[pgc_w].mug_w = u3r_mug_words(mem_w, pag_wiz_i); - if ( 0 > c3_pwrite(pat_u->mem_i, mem_w, pag_siz_i, off_i) ) { - fprintf(stderr, "loom: patch save: %s\r\n", strerror(errno)); +#if 0 + u3l_log("protect a: page %d\r\n", pag_w); +#endif + _ce_patch_write_page(pat_u, pgc_w, mem_w); + + if ( -1 == mprotect(u3_Loom + (pag_w << u3a_page), + pag_siz_i, + PROT_READ) ) + { + fprintf(stderr, "loom: patch mprotect: %s\r\n", strerror(errno)); c3_assert(0); } + u3P.dit_w[blk_w] &= ~(1 << bit_w); pgc_w += 1; } return pgc_w; @@ -549,9 +634,25 @@ _ce_patch_save_page(u3_ce_patch* pat_u, /* _ce_patch_compose(): make and write current patch. */ static u3_ce_patch* -_ce_patch_compose(c3_w nor_w, c3_w sou_w) +_ce_patch_compose(void) { c3_w pgs_w = 0; + c3_w nor_w = 0; + c3_w sou_w = 0; + + /* Calculate number of saved pages, north and south. + */ + { + c3_w nwr_w, swu_w; + + u3m_water(&nwr_w, &swu_w); + + nor_w = (nwr_w + (pag_wiz_i - 1)) >> u3a_page; + sou_w = (swu_w + (pag_wiz_i - 1)) >> u3a_page; + + c3_assert( ((gar_pag_p >> u3a_page) >= nor_w) + && ((gar_pag_p >> u3a_page) <= (u3a_pages - (sou_w + 1))) ); + } #ifdef U3_SNAPSHOT_VALIDATION u3K.nor_w = nor_w; @@ -624,7 +725,8 @@ _ce_image_sync(u3e_image* img_u) { if ( -1 == c3_sync(img_u->fid_i) ) { fprintf(stderr, "loom: image (%s) sync failed: %s\r\n", - img_u->nam_c, strerror(errno)); + img_u->nam_c, + strerror(errno)); c3_assert(!"loom: image sync"); } } @@ -634,19 +736,11 @@ _ce_image_sync(u3e_image* img_u) static void _ce_image_resize(u3e_image* img_u, c3_w pgs_w) { - off_t off_i = (off_t)pgs_w << (u3a_page + 2); - if ( img_u->pgs_w > pgs_w ) { - if ( (off_i >> (u3a_page + 2)) != pgs_w ) { - fprintf(stderr, "loom: image (%s) truncate: " - "offset overflow (%" PRId64 ") for page %u\r\n", - img_u->nam_c, (c3_ds)off_i, pgs_w); - c3_assert(0); - } - - if ( ftruncate(img_u->fid_i, off_i) ) { + if ( ftruncate(img_u->fid_i, pgs_w << (u3a_page + 2)) ) { fprintf(stderr, "loom: image (%s) truncate: %s\r\n", - img_u->nam_c, strerror(errno)); + img_u->nam_c, + strerror(errno)); c3_assert(0); } } @@ -659,20 +753,31 @@ _ce_image_resize(u3e_image* img_u, c3_w pgs_w) static void _ce_patch_apply(u3_ce_patch* pat_u) { - c3_w i_w, pag_w, off_w, mem_w[pag_wiz_i]; - c3_i fid_i; - size_t rof_i, wof_i, siz_i = pag_siz_i; - ssize_t ret_i; + ssize_t ret_i; + c3_w i_w; // resize images // _ce_image_resize(&u3P.nor_u, pat_u->con_u->nor_w); _ce_image_resize(&u3P.sou_u, pat_u->con_u->sou_w); + // seek to begining of patch and images + // + if ( (-1 == lseek(pat_u->mem_i, 0, SEEK_SET)) + || (-1 == lseek(u3P.nor_u.fid_i, 0, SEEK_SET)) + || (-1 == lseek(u3P.sou_u.fid_i, 0, SEEK_SET)) ) + { + fprintf(stderr, "loom: patch apply seek 0: %s\r\n", strerror(errno)); + c3_assert(0); + } + // write patch pages into the appropriate image // for ( i_w = 0; i_w < pat_u->con_u->pgs_w; i_w++ ) { - pag_w = pat_u->con_u->mem_u[i_w].pag_w; + c3_w pag_w = pat_u->con_u->mem_u[i_w].pag_w; + c3_w mem_w[pag_wiz_i]; + c3_i fid_i; + c3_w off_w; if ( pag_w < pat_u->con_u->nor_w ) { fid_i = u3P.nor_u.fid_i; @@ -683,10 +788,7 @@ _ce_patch_apply(u3_ce_patch* pat_u) off_w = (u3P.pag_w - (pag_w + 1)); } - rof_i = (size_t)i_w << (u3a_page + 2); - wof_i = (size_t)off_w << (u3a_page + 2); - - if ( siz_i != (ret_i = c3_pread(pat_u->mem_i, mem_w, siz_i, rof_i))) { + if ( pag_siz_i != (ret_i = read(pat_u->mem_i, mem_w, pag_siz_i)) ) { if ( 0 < ret_i ) { fprintf(stderr, "loom: patch apply partial read: %zu\r\n", (size_t)ret_i); @@ -696,10 +798,21 @@ _ce_patch_apply(u3_ce_patch* pat_u) } c3_assert(0); } - - if ( 0 > c3_pwrite(fid_i, mem_w, siz_i, wof_i) ) { - fprintf(stderr, "loom: patch apply write: %s\r\n", strerror(errno)); - c3_assert(0); + else { + if ( -1 == lseek(fid_i, (off_w << (u3a_page + 2)), SEEK_SET) ) { + fprintf(stderr, "loom: patch apply seek: %s\r\n", strerror(errno)); + c3_assert(0); + } + if ( pag_siz_i != (ret_i = write(fid_i, mem_w, pag_siz_i)) ) { + if ( 0 < ret_i ) { + fprintf(stderr, "loom: patch apply partial write: %zu\r\n", + (size_t)ret_i); + } + else { + fprintf(stderr, "loom: patch apply write: %s\r\n", strerror(errno)); + } + c3_assert(0); + } } #if 0 u3l_log("apply: %d, %x\n", pag_w, u3r_mug_words(mem_w, pag_wiz_i)); @@ -707,294 +820,52 @@ _ce_patch_apply(u3_ce_patch* pat_u) } } -/* _ce_loom_track_north(): [pgs_w] clean, followed by [dif_w] dirty. -*/ -void -_ce_loom_track_north(c3_w pgs_w, c3_w dif_w) -{ - c3_w blk_w = pgs_w >> 5; - c3_w bit_w = pgs_w & 31; - c3_w off_w; - - memset((void*)u3P.dit_w, 0, blk_w << 2); - - if ( bit_w ) { - c3_w tib_w = 32 - bit_w; - c3_w dat_w = u3P.dit_w[blk_w]; - - dat_w &= 0xffffffff << bit_w; - - if ( dif_w <= tib_w ) { - dat_w |= ((1 << dif_w) - 1) << bit_w; - dif_w = 0; - } - else { - dat_w |= 0xffffffff << bit_w; - dif_w -= tib_w; - } - - u3P.dit_w[blk_w] = dat_w; - blk_w += 1; - } - - off_w = blk_w; - blk_w = dif_w >> 5; - bit_w = dif_w & 31; - - memset((void*)(u3P.dit_w + off_w), 0xff, blk_w << 2); - - if ( bit_w ) { - u3P.dit_w[off_w + blk_w] |= (1 << bit_w) - 1; - } -} - -/* _ce_loom_track_south(): [pgs_w] clean, preceded by [dif_w] dirty. -*/ -void -_ce_loom_track_south(c3_w pgs_w, c3_w dif_w) -{ - c3_w blk_w = pgs_w >> 5; - c3_w bit_w = pgs_w & 31; - c3_w bas_w = ((u3P.pag_w - pgs_w) + 31) >> 5; - c3_w off_w; - - memset((void*)(u3P.dit_w + bas_w), 0, blk_w << 2); - - // the following index subtractions (bas_w, off) are safe, - // so long as the south segment never includes all pages - // - if ( bit_w ) { - c3_w tib_w = 32 - bit_w; - c3_w dat_w = u3P.dit_w[--bas_w]; - - dat_w &= 0xffffffff >> bit_w; - - if ( dif_w <= tib_w ) { - dat_w |= ((1 << dif_w) - 1) << (tib_w - dif_w); - dif_w = 0; - } - else { - dat_w |= 0xffffffff >> bit_w; - dif_w -= tib_w; - } - - u3P.dit_w[bas_w] = dat_w; - } - - blk_w = dif_w >> 5; - bit_w = dif_w & 31; - off_w = bas_w - blk_w; - - memset((void*)(u3P.dit_w + off_w), 0xff, blk_w << 2); - - if ( bit_w ) { - u3P.dit_w[off_w - 1] |= ((1 << bit_w) - 1) << (32 - bit_w); - } -} - -/* _ce_loom_protect_north(): protect/track pages from the bottom of memory. +/* _ce_image_blit(): apply image to memory. */ static void -_ce_loom_protect_north(c3_w pgs_w, c3_w old_w) +_ce_image_blit(u3e_image* img_u, + c3_w* ptr_w, + c3_ws stp_ws) { - if ( pgs_w ) { - if ( 0 != mprotect((void*)u3_Loom, - (size_t)pgs_w << (u3a_page + 2), - PROT_READ) ) - { - fprintf(stderr, "loom: pure north (%u pages): %s\r\n", - pgs_w, strerror(errno)); - c3_assert(0); - } + if ( 0 == img_u->pgs_w ) { + return; } - if ( old_w > pgs_w ) { - c3_w dif_w = old_w - pgs_w; - - if ( 0 != mprotect((void*)(u3_Loom + (pgs_w << u3a_page)), - (size_t)dif_w << (u3a_page + 2), - (PROT_READ | PROT_WRITE)) ) - { - fprintf(stderr, "loom: foul north (%u pages, %u old): %s\r\n", - pgs_w, old_w, strerror(errno)); - c3_assert(0); - } - -#ifdef U3_GUARD_PAGE - // protect guard page if clobbered - // - // NB: < pgs_w is precluded by assertion in u3e_save() - // - if ( u3P.gar_w < old_w ) { - fprintf(stderr, "loom: guard on reprotect\r\n"); - c3_assert( !_ce_ward_protect() ); - } -#endif - - _ce_loom_track_north(pgs_w, dif_w); - } - else { - _ce_loom_track_north(pgs_w, 0); - } -} - -/* _ce_loom_protect_south(): protect/track pages from the top of memory. -*/ -static void -_ce_loom_protect_south(c3_w pgs_w, c3_w old_w) -{ - if ( pgs_w ) { - if ( 0 != mprotect((void*)(u3_Loom + ((u3P.pag_w - pgs_w) << u3a_page)), - (size_t)pgs_w << (u3a_page + 2), - PROT_READ) ) - { - fprintf(stderr, "loom: pure south (%u pages): %s\r\n", - pgs_w, strerror(errno)); - c3_assert(0); - } - } - - if ( old_w > pgs_w ) { - c3_w dif_w = old_w - pgs_w; - c3_w off_w = u3P.pag_w - old_w; - - if ( 0 != mprotect((void*)(u3_Loom + (off_w << u3a_page)), - (size_t)dif_w << (u3a_page + 2), - (PROT_READ | PROT_WRITE)) ) - { - fprintf(stderr, "loom: foul south (%u pages, %u old): %s\r\n", - pgs_w, old_w, strerror(errno)); - c3_assert(0); - } - -#ifdef U3_GUARD_PAGE - // protect guard page if clobbered - // - // NB: >= pgs_w is precluded by assertion in u3e_save() - // - if ( u3P.gar_w >= off_w ) { - fprintf(stderr, "loom: guard on reprotect\r\n"); - c3_assert( !_ce_ward_protect() ); - } -#endif - - _ce_loom_track_south(pgs_w, dif_w); - } - else { - _ce_loom_track_south(pgs_w, 0); - } -} - -/* _ce_loom_mapf_north(): map [pgs_w] of [fid_i] into the bottom of memory -** (and anonymize [old_w - pgs_w] after if needed). -** -** NB: _ce_loom_mapf_south() is possible, but it would make separate mappings -** for each page since the south segment is reversed on disk. -** in practice, the south segment is a single page (and always dirty); -** a file-backed mapping for it is just not worthwhile. -*/ -static void -_ce_loom_mapf_north(c3_i fid_i, c3_w pgs_w, c3_w old_w) -{ - // XX mingw doesn't support MAP_FIXED clobbering; - // will require explicit unmapping and related bookkeeping. - // - if ( pgs_w ) { - if ( MAP_FAILED == mmap((void*)u3_Loom, - (size_t)pgs_w << (u3a_page + 2), - PROT_READ, - (MAP_FIXED | MAP_PRIVATE), - fid_i, 0) ) - { - fprintf(stderr, "loom: file-backed mmap failed (%u pages): %s\r\n", - pgs_w, strerror(errno)); - c3_assert(0); - } - } - - if ( old_w > pgs_w ) { - c3_w dif_w = old_w - pgs_w; - - if ( MAP_FAILED == mmap((void*)(u3_Loom + (pgs_w << u3a_page)), - (size_t)dif_w << (u3a_page + 2), - (PROT_READ | PROT_WRITE), - (MAP_ANON | MAP_FIXED | MAP_PRIVATE), - -1, 0) ) - { - fprintf(stderr, "loom: anonymous mmap failed (%u pages, %u old): %s\r\n", - pgs_w, old_w, strerror(errno)); - c3_assert(0); - } - -#ifdef U3_GUARD_PAGE - // protect guard page if clobbered - // - // NB: < pgs_w is precluded by assertion in u3e_save() - // - if ( u3P.gar_w < old_w ) { - fprintf(stderr, "loom: guard on remap\r\n"); - c3_assert( !_ce_ward_protect() ); - } -#endif - - _ce_loom_track_north(pgs_w, dif_w); - } - else { - _ce_loom_track_north(pgs_w, 0); - } -} - -/* _ce_loom_blit_north(): apply pages, in order, from the bottom of memory. -*/ -static void -_ce_loom_blit_north(c3_i fid_i, c3_w pgs_w) -{ - size_t len_i = (size_t)pgs_w << (u3a_page + 2); ssize_t ret_i; - - if ( pgs_w ) { - if ( len_i != (ret_i = c3_pread(fid_i, u3_Loom, len_i, 0)) ) { - if ( 0 < ret_i ) { - fprintf(stderr, "loom: blit north partial read: %zu\r\n", - (size_t)ret_i); - } - else { - fprintf(stderr, "loom: blit north read: %s\r\n", strerror(errno)); - } - c3_assert(0); - } - } - - _ce_loom_protect_north(pgs_w, 0); -} - -/* _ce_loom_blit_south(): apply pages, reversed, from the top of memory. -*/ -static void -_ce_loom_blit_south(c3_i fid_i, c3_w pgs_w) -{ c3_w i_w; - c3_w* ptr_w; - size_t off_i; - ssize_t ret_i; + c3_w siz_w = pag_siz_i; - for ( i_w = 0; i_w < pgs_w; i_w++ ) { - off_i = (size_t)i_w << (u3a_page + 2); - ptr_w = u3_Loom + ((u3P.pag_w - (i_w + 1)) << u3a_page); + if ( -1 == lseek(img_u->fid_i, 0, SEEK_SET) ) { + fprintf(stderr, "loom: image (%s) blit seek 0: %s\r\n", + img_u->nam_c, strerror(errno)); + c3_assert(0); + } - if ( pag_siz_i != (ret_i = c3_pread(fid_i, ptr_w, pag_siz_i, off_i)) ) { + for ( i_w = 0; i_w < img_u->pgs_w; i_w++ ) { + if ( siz_w != (ret_i = read(img_u->fid_i, ptr_w, siz_w)) ) { if ( 0 < ret_i ) { - fprintf(stderr, "loom: blit south partial read: %zu\r\n", - (size_t)ret_i); + fprintf(stderr, "loom: image (%s) blit partial read: %zu\r\n", + img_u->nam_c, (size_t)ret_i); } else { - fprintf(stderr, "loom: blit south read: %s\r\n", strerror(errno)); + fprintf(stderr, "loom: image (%s) blit read: %s\r\n", + img_u->nam_c, strerror(errno)); } c3_assert(0); } - } - _ce_loom_protect_south(pgs_w, 0); + if ( 0 != mprotect(ptr_w, siz_w, PROT_READ) ) { + fprintf(stderr, "loom: live mprotect: %s\r\n", strerror(errno)); + c3_assert(0); + } + + c3_w pag_w = u3a_outa(ptr_w) >> u3a_page; + c3_w blk_w = pag_w >> 5; + c3_w bit_w = pag_w & 31; + u3P.dit_w[blk_w] &= ~(1 << bit_w); + + ptr_w += stp_ws; + } } #ifdef U3_SNAPSHOT_VALIDATION @@ -1002,18 +873,22 @@ _ce_loom_blit_south(c3_i fid_i, c3_w pgs_w) */ static void _ce_image_fine(u3e_image* img_u, - c3_w* ptr_w, - c3_ws stp_ws) + c3_w* ptr_w, + c3_ws stp_ws) { - c3_w i_w, mem_w, fil_w; - c3_w buf_w[pag_wiz_i]; - size_t off_i, siz_i = pag_siz_i; - ssize_t ret_i; + ssize_t ret_i; + c3_w i_w; + c3_w buf_w[pag_wiz_i]; + + if ( -1 == lseek(img_u->fid_i, 0, SEEK_SET) ) { + fprintf(stderr, "loom: image fine seek 0: %s\r\n", strerror(errno)); + c3_assert(0); + } for ( i_w=0; i_w < img_u->pgs_w; i_w++ ) { - off_i = (size_t)i_w << (u3a_page + 2); + c3_w mem_w, fil_w; - if ( siz_i != (ret_i = c3_pread(img_u->fid_i, buf_w, siz_i, off_i)) ) { + if ( pag_siz_i != (ret_i = read(img_u->fid_i, buf_w, pag_siz_i)) ) { if ( 0 < ret_i ) { fprintf(stderr, "loom: image (%s) fine partial read: %zu\r\n", img_u->nam_c, (size_t)ret_i); @@ -1024,7 +899,6 @@ _ce_image_fine(u3e_image* img_u, } c3_assert(0); } - mem_w = u3r_mug_words(ptr_w, pag_wiz_i); fil_w = u3r_mug_words(buf_w, pag_wiz_i); @@ -1045,28 +919,36 @@ _ce_image_fine(u3e_image* img_u, } #endif -/* _ce_image_copy(): copy all of [fom_u] to [tou_u] -** -** XX use reflinks a la _king_copy_file()? +/* _ce_image_copy(): */ static c3_o _ce_image_copy(u3e_image* fom_u, u3e_image* tou_u) { - c3_w i_w; - c3_w mem_w[pag_wiz_i]; - size_t off_i, siz_i = pag_siz_i; ssize_t ret_i; + c3_w i_w; // resize images // _ce_image_resize(tou_u, fom_u->pgs_w); + // seek to begining of patch and images + // + if ( (-1 == lseek(fom_u->fid_i, 0, SEEK_SET)) + || (-1 == lseek(tou_u->fid_i, 0, SEEK_SET)) ) + { + fprintf(stderr, "loom: image (%s) copy seek: %s\r\n", + fom_u->nam_c, + strerror(errno)); + return c3n; + } + // copy pages into destination image // for ( i_w = 0; i_w < fom_u->pgs_w; i_w++ ) { - off_i = (size_t)i_w << (u3a_page + 2); + c3_w mem_w[pag_wiz_i]; + c3_w off_w = i_w; - if ( siz_i != (ret_i = c3_pread(fom_u->fid_i, mem_w, siz_i, off_i)) ) { + if ( pag_siz_i != (ret_i = read(fom_u->fid_i, mem_w, pag_siz_i)) ) { if ( 0 < ret_i ) { fprintf(stderr, "loom: image (%s) copy partial read: %zu\r\n", fom_u->nam_c, (size_t)ret_i); @@ -1077,30 +959,40 @@ _ce_image_copy(u3e_image* fom_u, u3e_image* tou_u) } return c3n; } - - if ( 0 > c3_pwrite(tou_u->fid_i, mem_w, siz_i, off_i) ) { - fprintf(stderr, "loom: image (%s) copy write: %s\r\n", - tou_u->nam_c, strerror(errno)); - return c3n; + else { + if ( -1 == lseek(tou_u->fid_i, (off_w << (u3a_page + 2)), SEEK_SET) ) { + fprintf(stderr, "loom: image (%s) copy seek: %s\r\n", + tou_u->nam_c, strerror(errno)); + return c3n; + } + if ( pag_siz_i != (ret_i = write(tou_u->fid_i, mem_w, pag_siz_i)) ) { + if ( 0 < ret_i ) { + fprintf(stderr, "loom: image (%s) copy partial write: %zu\r\n", + tou_u->nam_c, (size_t)ret_i); + } + else { + fprintf(stderr, "loom: image (%s) copy write: %s\r\n", + tou_u->nam_c, strerror(errno)); + } + return c3n; + } } } return c3y; } -/* _ce_backup(); copy snapshot to .urb/bhk (if it doesn't exist yet). +/* _ce_backup(); */ static void _ce_backup(void) { u3e_image nop_u = { .nam_c = "north", .pgs_w = 0 }; u3e_image sop_u = { .nam_c = "south", .pgs_w = 0 }; - c3_i mod_i = O_RDWR | O_CREAT; // XX O_TRUNC ? + c3_i mod_i = O_RDWR | O_CREAT; c3_c ful_c[8193]; - // XX move directory creation? - // - snprintf(ful_c, 8192, "%s/.urb/bhk", u3C.dir_c); + snprintf(ful_c, 8192, "%s/.urb/bhk", u3P.dir_c); if ( c3_mkdir(ful_c, 0700) ) { if ( EEXIST != errno ) { @@ -1109,14 +1001,14 @@ _ce_backup(void) return; } - snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3C.dir_c, nop_u.nam_c); + snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3P.dir_c, nop_u.nam_c); if ( -1 == (nop_u.fid_i = c3_open(ful_c, mod_i, 0666)) ) { fprintf(stderr, "loom: c3_open %s: %s\r\n", ful_c, strerror(errno)); return; } - snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3C.dir_c, sop_u.nam_c); + snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3P.dir_c, sop_u.nam_c); if ( -1 == (sop_u.fid_i = c3_open(ful_c, mod_i, 0666)) ) { fprintf(stderr, "loom: c3_open %s: %s\r\n", ful_c, strerror(errno)); @@ -1128,9 +1020,9 @@ _ce_backup(void) { c3_unlink(ful_c); - snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3C.dir_c, nop_u.nam_c); + snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3P.dir_c, nop_u.nam_c); c3_unlink(ful_c); - snprintf(ful_c, 8192, "%s/.urb/bhk", u3C.dir_c); + snprintf(ful_c, 8192, "%s/.urb/bhk", u3P.dir_c); c3_rmdir(ful_c); } @@ -1159,29 +1051,19 @@ _ce_backup(void) before we try to make another snapshot. */ void -u3e_save(u3_post low_p, u3_post hig_p) +u3e_save(void) { u3_ce_patch* pat_u; - c3_w nod_w, sod_w; if ( u3C.wag_w & u3o_dryrun ) { return; } - { - c3_w nop_w = (low_p >> u3a_page); - c3_w nor_w = (low_p + (pag_wiz_i - 1)) >> u3a_page; - c3_w sop_w = hig_p >> u3a_page; - - c3_assert( (u3P.gar_w > nop_w) && (u3P.gar_w < sop_w) ); - - if ( !(pat_u = _ce_patch_compose(nor_w, u3P.pag_w - sop_w)) ) { - return; - } + if ( !(pat_u = _ce_patch_compose()) ) { + return; } - nod_w = u3P.nor_u.pgs_w; - sod_w = u3P.sou_u.pgs_w; + // u3a_print_memory(stderr, "sync: save", 4096 * pat_u->con_u->pgs_w); _ce_patch_sync(pat_u); @@ -1206,15 +1088,6 @@ u3e_save(u3_post low_p, u3_post hig_p) } #endif - if ( u3C.wag_w & u3o_no_demand ) { - _ce_loom_protect_north(u3P.nor_u.pgs_w, nod_w); - } - else { - _ce_loom_mapf_north(u3P.nor_u.fid_i, u3P.nor_u.pgs_w, nod_w); - } - - _ce_loom_protect_south(u3P.sou_u.pgs_w, sod_w); - _ce_image_sync(&u3P.nor_u); _ce_image_sync(&u3P.sou_u); _ce_patch_free(pat_u); @@ -1226,16 +1099,8 @@ u3e_save(u3_post low_p, u3_post hig_p) /* u3e_live(): start the checkpointing system. */ c3_o -u3e_live(c3_c* dir_c) +u3e_live(c3_o nuu_o, c3_c* dir_c) { - c3_o nuu_o = c3n; - - // XX demand paging is not supported on windows - // -#ifdef U3_OS_mingw - u3C.wag_w |= u3o_no_demand; -#endif - // require that our page size is a multiple of the system page size. // { @@ -1271,7 +1136,6 @@ u3e_live(c3_c* dir_c) } else { u3_ce_patch* pat_u; - c3_w nor_w, sou_w; /* Load any patch files; apply them to images. */ @@ -1283,52 +1147,41 @@ u3e_live(c3_c* dir_c) _ce_patch_delete(); } - nor_w = u3P.nor_u.pgs_w; - sou_w = u3P.sou_u.pgs_w; - // detect snapshots from a larger loom // - if ( (nor_w + sou_w + 1) >= u3P.pag_w ) { + if ( (u3P.nor_u.pgs_w + u3P.sou_u.pgs_w + 1) >= u3a_pages ) { fprintf(stderr, "boot: snapshot too big for loom\r\n"); exit(1); } // mark all pages dirty (pages in the snapshot will be marked clean) // - _ce_loom_track_north(0, u3P.pag_w); + u3e_foul(); /* Write image files to memory; reinstate protection. */ { - if ( u3C.wag_w & u3o_no_demand ) { - _ce_loom_blit_north(u3P.nor_u.fid_i, nor_w); - } - else { - _ce_loom_mapf_north(u3P.nor_u.fid_i, nor_w, 0); - } + _ce_image_blit(&u3P.nor_u, + u3_Loom, + pag_wiz_i); - _ce_loom_blit_south(u3P.sou_u.fid_i, sou_w); + _ce_image_blit(&u3P.sou_u, + (u3_Loom + u3C.wor_i) - pag_wiz_i, + -(ssize_t)pag_wiz_i); u3l_log("boot: protected loom\r\n"); } /* If the images were empty, we are logically booting. */ - if ( !nor_w && !sou_w ) { + if ( (0 == u3P.nor_u.pgs_w) && (0 == u3P.sou_u.pgs_w) ) { u3l_log("live: logical boot\r\n"); nuu_o = c3y; } - else if ( u3C.wag_w & u3o_no_demand ) { - u3a_print_memory(stderr, "live: loaded", (nor_w + sou_w) << u3a_page); - } else { - u3a_print_memory(stderr, "live: mapped", nor_w << u3a_page); - u3a_print_memory(stderr, "live: loaded", sou_w << u3a_page); + u3a_print_memory(stderr, "live: loaded", + (u3P.nor_u.pgs_w + u3P.sou_u.pgs_w) << u3a_page); } - -#ifdef U3_GUARD_PAGE - c3_assert( !_ce_ward_post(nor_w, u3P.pag_w - sou_w) ); -#endif } } @@ -1352,17 +1205,23 @@ u3e_yolo(void) return c3n; } -#ifdef U3_GUARD_PAGE - c3_assert( !_ce_ward_protect() ); -#endif - - // mark all pages dirty - // - _ce_loom_track_north(0, u3P.pag_w); + if ( 0 != mprotect(u3a_into(gar_pag_p), pag_siz_i, PROT_NONE) ) { + fprintf(stderr, "loom: failed to protect guard page: %s\r\n", + strerror(errno)); + c3_assert(0); + } return c3y; } +/* u3e_foul(): dirty all the pages of the loom. +*/ +void +u3e_foul(void) +{ + memset((void*)u3P.dit_w, 0xff, sizeof(u3P.dit_w)); +} + /* u3e_init(): initialize guard page tracking. */ void @@ -1371,10 +1230,8 @@ u3e_init(void) u3P.pag_w = u3C.wor_i >> u3a_page; #ifdef U3_GUARD_PAGE - c3_assert( !_ce_ward_post(0, u3P.pag_w) ); + _ce_center_guard_page(); #endif - - _ce_loom_track_north(0, u3P.pag_w); } /* u3e_ward(): reposition guard page if needed. @@ -1383,14 +1240,8 @@ void u3e_ward(u3_post low_p, u3_post hig_p) { #ifdef U3_GUARD_PAGE - c3_w nop_w = low_p >> u3a_page; - c3_w sop_w = hig_p >> u3a_page; - c3_w pag_w = u3P.gar_w; - - if ( !((pag_w > nop_w) && (pag_w < hig_p)) ) { - c3_assert( !_ce_ward_post(nop_w, sop_w) ); - c3_assert( !_ce_flaw_protect(pag_w) ); - c3_assert( u3P.dit_w[pag_w >> 5] & (1 << (pag_w & 31)) ); + if ( (low_p > gar_pag_p) || (hig_p < gar_pag_p) ) { + _ce_center_guard_page(); } #endif } diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index bc868309b..33b57a43a 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -2,7 +2,6 @@ ** */ #include "all.h" -#include "noun/events.h" #include "rsignal.h" #include "vere/vere.h" #include @@ -437,7 +436,7 @@ u3m_file(c3_c* pas_c) u3l_log("Loading file: %s\r\n", pas_c); struct stat buf_b; c3_i fid_i = c3_open(pas_c, O_RDONLY, 0644); - c3_w fln_w; + c3_w fln_w, red_w; c3_y* pad_y; if ( (fid_i < 0) || (fstat(fid_i, &buf_b) < 0) ) { @@ -449,10 +448,10 @@ u3m_file(c3_c* pas_c) pad_y = c3_malloc(buf_b.st_size); - ssize_t red_i = c3_pread(fid_i, pad_y, fln_w, 0); + red_w = read(fid_i, pad_y, fln_w); close(fid_i); - if ( red_i != fln_w ) { + if ( fln_w != red_w ) { c3_free(pad_y); return u3m_bail(c3__fail); } @@ -528,8 +527,6 @@ _pave_north(c3_w* mem_w, c3_w siz_w, c3_w len_w) // the stack starts at the end of the memory segment, // minus space for the road structure [siz_w] // - // NB: the returned road is "above" the stack - // c3_w* rut_w = mem_w; c3_w* mat_w = ((mem_w + len_w) - siz_w); c3_w* cap_w = mat_w; @@ -548,8 +545,6 @@ _pave_south(c3_w* mem_w, c3_w siz_w, c3_w len_w) // the stack starts at the base memory pointer [mem_w], // and ends after the space for the road structure [siz_w] // - // NB: the returned road is *on* the stack - // c3_w* rut_w = (mem_w + len_w); c3_w* mat_w = mem_w; c3_w* cap_w = mat_w + siz_w; @@ -605,7 +600,7 @@ _find_home(void) // this looks risky, but there are no legitimate scenarios // where it's wrong // - u3R->cap_p = u3R->mat_p = u3a_outa(u3H); + u3R->cap_p = u3R->mat_p = u3C.wor_i - c3_wiseof(*u3H); } /* u3m_pave(): instantiate or activate image. @@ -824,6 +819,7 @@ u3m_leap(c3_w pad_w) u3R->cap_p -= len_w; rod_u = _pave_south(u3a_into(bot_p), c3_wiseof(u3a_road), len_w); + u3e_ward(rod_u->cap_p, rod_u->hat_p); #if 0 fprintf(stderr, "leap: from north %p (cap 0x%x), to south %p\r\n", u3R, @@ -836,6 +832,7 @@ u3m_leap(c3_w pad_w) u3R->cap_p += len_w; rod_u = _pave_north(u3a_into(bot_p), c3_wiseof(u3a_road), len_w); + u3e_ward(rod_u->hat_p, rod_u->cap_p); #if 0 fprintf(stderr, "leap: from south %p (cap 0x%x), to north %p\r\n", u3R, @@ -857,7 +854,6 @@ u3m_leap(c3_w pad_w) */ { u3R = rod_u; - u3m_ward(); _pave_parts(); } #ifdef U3_MEMORY_DEBUG @@ -1007,31 +1003,12 @@ u3m_flog(c3_w gof_w) /* u3m_water(): produce watermarks. */ void -u3m_water(u3_post* low_p, u3_post* hig_p) +u3m_water(c3_w* low_w, c3_w* hig_w) { - // allow the segfault handler to fire before the road is set - // - // while not explicitly possible in the codebase, - // compiler optimizations can reorder stores - // - if ( !u3R ) { - *low_p = 0; - *hig_p = u3C.wor_i - 1; - } - // in a north road, hat points to the end of the heap + 1 word, - // while cap points to the top of the stack - // - else if ( c3y == u3a_is_north(u3R) ) { - *low_p = u3R->hat_p - 1; - *hig_p = u3R->cap_p; - } - // in a south road, hat points to the end of the heap, - // while cap points to the top of the stack + 1 word - // - else { - *low_p = u3R->cap_p - 1; - *hig_p = u3R->hat_p; - } + c3_assert(u3R == &u3H->rod_u); + + *low_w = u3a_heap(u3R); + *hig_w = u3a_temp(u3R) + c3_wiseof(u3v_home); } /* u3m_soft_top(): top-level safety wrapper. @@ -1715,80 +1692,6 @@ _cm_limits(void) # endif } -/* u3m_fault(): handle a memory event with libsigsegv protocol. -*/ -c3_i -u3m_fault(void* adr_v, c3_i ser_i) -{ - // let the stack overflow handler run. - // - if ( 0 == ser_i ) { - return 0; - } - - c3_w* adr_w = (c3_w*)adr_v; - u3_post low_p, hig_p; - - if ( (adr_w < u3_Loom) || (adr_w >= (u3_Loom + u3C.wor_i)) ) { - fprintf(stderr, "loom: external fault: %p (%p : %p)\r\n\r\n", - adr_w, u3_Loom, u3_Loom + u3C.wor_i); - c3_assert(0); - return 0; - } - - u3m_water(&low_p, &hig_p); - - switch ( u3e_fault(low_p, hig_p, u3a_outa(adr_w)) ) { - // page tracking invariants violated, fatal - // - case u3e_flaw_sham: { - c3_assert(0); - return 0; - } - - // virtual memory failure (protections), possibly recoverable XX - // - case u3e_flaw_base: { - c3_assert(0); - return 0; - } - - // loom limits exceeded, recoverable - // - case u3e_flaw_meme: { - u3m_signal(c3__meme); // doesn't return - return 1; - } - - case u3e_flaw_good: return 1; - } - - c3_assert(!"unpossible"); -} - -/* u3m_save(): update the checkpoint. -*/ -void -u3m_save(void) -{ - u3_post low_p, hig_p; - u3m_water(&low_p, &hig_p); - - c3_assert(u3R == &u3H->rod_u); - - return u3e_save(low_p, hig_p); -} - -/* u3m_ward(): tend the guard page. -*/ -void -u3m_ward(void) -{ - u3_post low_p, hig_p; - u3m_water(&low_p, &hig_p); - return u3e_ward(low_p, hig_p); -} - /* _cm_signals(): set up interrupts, etc. */ static void @@ -1804,7 +1707,7 @@ _cm_signals(void) // filter (see compat/mingw/seh_handler.c) that handles both memory // access and stack overflow exceptions. It calls u3e_fault directly. # else - if ( 0 != sigsegv_install_handler(u3m_fault) ) { + if ( 0 != sigsegv_install_handler(u3e_fault) ) { u3l_log("boot: sigsegv install failed\n"); exit(1); } @@ -1961,46 +1864,6 @@ void u3m_stop() { u3je_secp_stop(); - - // XX move to jets.c - // - c3_free(u3D.ray_u); -} - -/* u3m_pier(): make a pier. -*/ -c3_c* -u3m_pier(c3_c* dir_c) -{ - c3_c ful_c[8193]; - - u3C.dir_c = dir_c; - - snprintf(ful_c, 8192, "%s", dir_c); - if ( c3_mkdir(ful_c, 0700) ) { - if ( EEXIST != errno ) { - fprintf(stderr, "loom: pier create: %s\r\n", strerror(errno)); - c3_assert(0); - } - } - - snprintf(ful_c, 8192, "%s/.urb", dir_c); - if ( c3_mkdir(ful_c, 0700) ) { - if ( EEXIST != errno ) { - fprintf(stderr, "loom: .urb create: %s\r\n", strerror(errno)); - c3_assert(0); - } - } - - snprintf(ful_c, 8192, "%s/.urb/chk", dir_c); - if ( c3_mkdir(ful_c, 0700) ) { - if ( EEXIST != errno ) { - fprintf(stderr, "loom: .urb/chk create: %s\r\n", strerror(errno)); - c3_assert(0); - } - } - - return strdup(ful_c); } /* u3m_boot(): start the u3 system. return next event, starting from 1. @@ -2016,7 +1879,7 @@ u3m_boot(c3_c* dir_c, size_t len_i) /* Activate the storage system. */ - nuu_o = u3e_live(u3m_pier(dir_c)); + nuu_o = u3e_live(c3n, dir_c); /* Activate tracing. */ @@ -2029,6 +1892,10 @@ u3m_boot(c3_c* dir_c, size_t len_i) */ u3m_pave(nuu_o); + /* Place the guard page. + */ + u3e_init(); + /* Initialize the jet system. */ { diff --git a/pkg/urbit/noun/trace.c b/pkg/urbit/noun/trace.c index 85afcfd46..79d290930 100644 --- a/pkg/urbit/noun/trace.c +++ b/pkg/urbit/noun/trace.c @@ -278,19 +278,11 @@ void u3t_trace_open(c3_c* dir_c) { c3_c fil_c[2048]; - - if ( !dir_c ) { - return; - } - snprintf(fil_c, 2048, "%s/.urb/put/trace", dir_c); struct stat st; - if ( (-1 == stat(fil_c, &st)) - && (-1 == c3_mkdir(fil_c, 0700)) ) - { - fprintf(stderr, "mkdir: %s failed: %s\r\n", fil_c, strerror(errno)); - return; + if ( -1 == stat(fil_c, &st) ) { + c3_mkdir(fil_c, 0700); } c3_c lif_c[2056]; @@ -299,11 +291,6 @@ u3t_trace_open(c3_c* dir_c) u3_Host.tra_u.fil_u = c3_fopen(lif_c, "w"); u3_Host.tra_u.nid_w = (int)getpid(); - if ( !u3_Host.tra_u.fil_u) { - fprintf(stderr, "trace open: %s\r\n", strerror(errno)); - return; - } - fprintf(u3_Host.tra_u.fil_u, "[ "); // We have two "threads", the event processing and the nock stuff. diff --git a/pkg/urbit/noun/urth.c b/pkg/urbit/noun/urth.c index de4e2d560..30ab44817 100644 --- a/pkg/urbit/noun/urth.c +++ b/pkg/urbit/noun/urth.c @@ -2,7 +2,6 @@ ** */ #include "all.h" -#include "noun/events.h" #include "ur/ur.h" #include #include @@ -417,6 +416,10 @@ _cu_realloc(FILE* fil_u, ur_root_t** tor_u, ur_nvec_t* doc_u) // u3A->eve_d = eve_d; + // mark all pages dirty + // + u3e_foul(); + *tor_u = rot_u; *doc_u = cod_u; @@ -426,17 +429,15 @@ _cu_realloc(FILE* fil_u, ur_root_t** tor_u, ur_nvec_t* doc_u) /* u3u_meld(): globally deduplicate memory. */ #ifdef U3_MEMORY_DEBUG -c3_w +void u3u_meld(void) { fprintf(stderr, "u3: unable to meld under U3_MEMORY_DEBUG\r\n"); - return 0; } #else -c3_w +void u3u_meld(void) { - c3_w pre_w = u3a_open(u3R); ur_root_t* rot_u; ur_nvec_t cod_u; @@ -448,8 +449,6 @@ u3u_meld(void) // ur_nvec_free(&cod_u); ur_root_free(rot_u); - - return (u3a_open(u3R) - pre_w); } #endif @@ -565,14 +564,56 @@ _cu_rock_save(c3_c* dir_c, c3_d eve_d, c3_d len_d, c3_y* byt_y) // write jam-buffer into [fid_i] // - ssize_t rit_i = c3_pwrite(fid_i, byt_y, len_d, 0); - if ( rit_i < 0 ) { - fprintf(stderr, "rock: write failed: %s\r\n", strerror(errno)); + // XX deduplicate with _write() wrapper in term.c + // + { + ssize_t ret_i; + + while ( len_d > 0 ) { + c3_w lop_w = 0; + // retry interrupt/async errors + // + do { + // abort pathological retry loop + // + if ( 100 == ++lop_w ) { + fprintf(stderr, "rock: write loop: %s\r\n", strerror(errno)); + close(fid_i); + // XX unlink file? + // + return c3n; + } + + ret_i = write(fid_i, byt_y, len_d); + } + while ( (ret_i < 0) + && ( (errno == EINTR) + || (errno == EAGAIN) + || (errno == EWOULDBLOCK) )); + + // assert on true errors + // + // NB: can't call u3l_log here or we would re-enter _write() + // + if ( ret_i < 0 ) { + fprintf(stderr, "rock: write failed %s\r\n", strerror(errno)); + close(fid_i); + // XX unlink file? + // + return c3n; + } + // continue partial writes + // + else { + len_d -= ret_i; + byt_y += ret_i; + } + } } close(fid_i); - return rit_i < 0 ? c3n : c3y; + return c3y; } /* u3u_cram(): globably deduplicate memory, and write a rock to disk. @@ -848,6 +889,10 @@ u3u_uncram(c3_c* dir_c, c3_d eve_d) // u3A->eve_d = eve_d; + // mark all pages dirty + // + u3e_foul(); + // leave rocks on disk // // if ( 0 != c3_unlink(nam_c) ) { diff --git a/pkg/urbit/noun/vortex.c b/pkg/urbit/noun/vortex.c index a37b76888..016588ea8 100644 --- a/pkg/urbit/noun/vortex.c +++ b/pkg/urbit/noun/vortex.c @@ -27,18 +27,9 @@ u3v_life(u3_noun eve) c3_o u3v_boot(u3_noun eve) { - c3_d len_d; - - { - u3_noun len = u3qb_lent(eve); - c3_assert( c3y == u3r_safe_chub(len, &len_d) ); - u3z(len); - } - // ensure zero-initialized kernel // - u3A->roc = 0; - u3A->eve_d = 0; + u3A->roc = 0; { u3_noun pro = u3m_soft(0, u3v_life, eve); @@ -48,8 +39,7 @@ u3v_boot(u3_noun eve) return c3n; } - u3A->roc = u3k(u3t(pro)); - u3A->eve_d = len_d; + u3A->roc = u3k(u3t(pro)); u3z(pro); } @@ -270,61 +260,6 @@ u3v_poke(u3_noun ovo) return pro; } -/* _cv_poke_eve(): u3v_poke w/out u3A->now XX replace -*/ -static u3_noun -_cv_poke_eve(u3_noun sam) -{ - u3_noun fun = u3n_nock_on(u3k(u3A->roc), u3k(u3x_at(_CVX_POKE, u3A->roc))); - u3_noun pro; - - { -# ifdef U3_MEMORY_DEBUG - c3_w cod_w = u3a_lush(u3h(u3t(u3t(sam)))); -# endif - - pro = u3n_slam_on(fun, sam); - -# ifdef U3_MEMORY_DEBUG - u3a_lop(cod_w); -# endif - } - - return pro; -} - -/* u3v_poke_sure(): inject an event, saving new state if successful. -*/ -c3_o -u3v_poke_sure(c3_w mil_w, u3_noun eve, u3_noun* pro) -{ - u3_noun gon = u3m_soft(mil_w, _cv_poke_eve, eve); - u3_noun tag, dat; - u3x_cell(gon, &tag, &dat); - - // event failed, produce trace - // - if ( u3_blip != tag ) { - *pro = gon; - return c3n; - } - - // event succeeded, persist state and produce effects - // - { - u3_noun vir, cor; - u3x_cell(dat, &vir, &cor); - - u3z(u3A->roc); - u3A->roc = u3k(cor); - u3A->eve_d++; - - *pro = u3k(vir); - u3z(gon); - return c3y; - } -} - /* u3v_tank(): dump single tank. */ void diff --git a/pkg/urbit/tests/ames_tests.c b/pkg/urbit/tests/ames_tests.c index 38581d8ca..0907898d4 100644 --- a/pkg/urbit/tests/ames_tests.c +++ b/pkg/urbit/tests/ames_tests.c @@ -6,7 +6,8 @@ static void _setup(void) { - u3m_boot_lite(1 << 22); + u3m_init(1 << 22); + u3m_pave(c3y); } /* _test_ames(): spot check ames helpers diff --git a/pkg/urbit/tests/events_tests.c b/pkg/urbit/tests/events_tests.c deleted file mode 100644 index 92f1bd2fe..000000000 --- a/pkg/urbit/tests/events_tests.c +++ /dev/null @@ -1,251 +0,0 @@ -#include "all.h" -#include "noun/events.h" - -/* _setup(): prepare for tests. -*/ -static void -_setup(void) -{ - // NB: no loom - // - u3P.pag_w = u3a_pages; -} - -static c3_w -_check_north_clean(void) -{ - c3_w i_w, pag_w, blk_w, bit_w; - - for ( i_w = 0; i_w < u3P.pag_w; i_w++ ) { - pag_w = i_w; - blk_w = pag_w >> 5; - bit_w = pag_w & 31; - - if ( u3P.dit_w[blk_w] & (1 << bit_w) ) { - break; - } - } - - return i_w; -} - -static c3_w -_check_north_dirty(c3_w pgs_w, c3_w max_w) -{ - c3_w i_w, pag_w, blk_w, bit_w; - - for ( i_w = 0; i_w < max_w; i_w++ ) { - pag_w = i_w + pgs_w; - blk_w = pag_w >> 5; - bit_w = pag_w & 31; - - if ( !(u3P.dit_w[blk_w] & (1 << bit_w)) ) { - break; - } - } - - return i_w; -} - -static c3_w -_check_south_clean(void) -{ - c3_w i_w, pag_w, blk_w, bit_w; - - for ( i_w = 0; i_w < u3P.pag_w; i_w++ ) { - pag_w = u3P.pag_w - (i_w + 1); - blk_w = pag_w >> 5; - bit_w = pag_w & 31; - - if ( u3P.dit_w[blk_w] & (1 << bit_w) ) { - break; - } - } - - return i_w; -} - -static c3_w -_check_south_dirty(c3_w pgs_w, c3_w max_w) -{ - c3_w i_w, pag_w, blk_w, bit_w; - - for ( i_w = 0; i_w < max_w; i_w++ ) { - pag_w = u3P.pag_w - (i_w + pgs_w + 1); - blk_w = pag_w >> 5; - bit_w = pag_w & 31; - - if ( !(u3P.dit_w[blk_w] & (1 << bit_w)) ) { - break; - } - } - - return i_w; -} - -void -_ce_loom_track_north(c3_w pgs_w, c3_w dif_w); -void -_ce_loom_track_south(c3_w pgs_w, c3_w dif_w); - -static c3_i -_test_tracking(void) -{ - c3_w ret_w; - - _ce_loom_track_north(0, u3P.pag_w); - - if ( u3P.pag_w != (ret_w = _check_north_dirty(0, u3P.pag_w)) ) { - fprintf(stderr, "test events track north dirty all %u\r\n", ret_w); - return 0; - } - - if ( 0 != (ret_w = _check_north_clean()) ) { - fprintf(stderr, "test events track north init %u\r\n", ret_w); - return 0; - } - - if ( 0 != (ret_w = _check_south_clean()) ) { - fprintf(stderr, "test events track south init %u\r\n", ret_w); - return 0; - } - - _ce_loom_track_north(100, 0); - _ce_loom_track_south(1, 0); - - if ( 100 != (ret_w = _check_north_clean()) ) { - fprintf(stderr, "test events track north clean a %u\r\n", ret_w); - return 0; - } - - if ( 1 != (ret_w = _check_south_clean()) ) { - fprintf(stderr, "test events track south clean a %u\r\n", ret_w); - return 0; - } - - _ce_loom_track_north(75, 25); - _ce_loom_track_south(2, 0); - - if ( 75 != (ret_w = _check_north_clean()) ) { - fprintf(stderr, "test events track north clean b %u\r\n", ret_w); - return 0; - } - - if ( 25 != (ret_w = _check_north_dirty(75, 25)) ) { - fprintf(stderr, "test events track north dirty b %u\r\n", ret_w); - return 0; - } - - if ( 2 != (ret_w = _check_south_clean()) ) { - fprintf(stderr, "test events track south clean b %u\r\n", ret_w); - return 0; - } - - _ce_loom_track_north(55, 20); - _ce_loom_track_south(1, 1); - - if ( 55 != (ret_w = _check_north_clean()) ) { - fprintf(stderr, "test events track north clean c %u\r\n", ret_w); - return 0; - } - - if ( 20 != (ret_w = _check_north_dirty(55, 20)) ) { - fprintf(stderr, "test events track north dirty c %u\r\n", ret_w); - return 0; - } - - if ( 1 != (ret_w = _check_south_clean()) ) { - fprintf(stderr, "test events track south clean c %u\r\n", ret_w); - return 0; - } - - if ( 1 != (ret_w = _check_south_dirty(1, 1)) ) { - fprintf(stderr, "test events track north dirty c %u\r\n", ret_w); - return 0; - } - - _ce_loom_track_north(255, 0); - _ce_loom_track_south(48, 0); - - if ( 255 != (ret_w = _check_north_clean()) ) { - fprintf(stderr, "test events track north clean d %u\r\n", ret_w); - return 0; - } - - if ( 48 != (ret_w = _check_south_clean()) ) { - fprintf(stderr, "test events track south clean d %u\r\n", ret_w); - return 0; - } - - _ce_loom_track_north(213, 42); - _ce_loom_track_south(15, 33); - - if ( 213 != (ret_w = _check_north_clean()) ) { - fprintf(stderr, "test events track north clean e %u\r\n", ret_w); - return 0; - } - - if ( 42 != (ret_w = _check_north_dirty(213, 42)) ) { - fprintf(stderr, "test events track north dirty e %u\r\n", ret_w); - return 0; - } - - if ( 15 != (ret_w = _check_south_clean()) ) { - fprintf(stderr, "test events track south clean e %u\r\n", ret_w); - return 0; - } - - if ( 33 != (ret_w = _check_south_dirty(15, 33)) ) { - fprintf(stderr, "test events track north dirty e %u\r\n", ret_w); - return 0; - } - - _ce_loom_track_north(200, 13); - _ce_loom_track_south(10, 5); - - if ( 200 != (ret_w = _check_north_clean()) ) { - fprintf(stderr, "test events track north clean f %u\r\n", ret_w); - return 0; - } - - if ( 13 != (ret_w = _check_north_dirty(200, 13)) ) { - fprintf(stderr, "test events track north dirty f %u\r\n", ret_w); - return 0; - } - - if ( 10 != (ret_w = _check_south_clean()) ) { - fprintf(stderr, "test events track south clean f %u\r\n", ret_w); - return 0; - } - - if ( 5 != (ret_w = _check_south_dirty(10, 5)) ) { - fprintf(stderr, "test events track north dirty f %u\r\n", ret_w); - return 0; - } - - _ce_loom_track_north(0, u3P.pag_w); - - if ( u3P.pag_w != (ret_w = _check_north_dirty(0, u3P.pag_w)) ) { - fprintf(stderr, "test events track north dirty all %u\r\n", ret_w); - return 0; - } - - return 1; -} - -/* main(): run all test cases. -*/ -int -main(int argc, char* argv[]) -{ - _setup(); - - if ( !_test_tracking() ) { - fprintf(stderr, "test_events: tracking: failed\r\n"); - exit(1); - } - - fprintf(stderr, "test_events: ok\n"); - - return 0; -} diff --git a/pkg/urbit/tests/hashtable_tests.c b/pkg/urbit/tests/hashtable_tests.c index 051ec709e..3b36dc887 100644 --- a/pkg/urbit/tests/hashtable_tests.c +++ b/pkg/urbit/tests/hashtable_tests.c @@ -9,7 +9,8 @@ c3_w _ch_skip_slot(c3_w mug_w, c3_w lef_w); static void _setup(void) { - u3m_boot_lite(1 << 26); + u3m_init(1 << 26); + u3m_pave(c3y); } /* _test_bit_manipulation(): diff --git a/pkg/urbit/tests/jam_tests.c b/pkg/urbit/tests/jam_tests.c index d32796855..fd416945d 100644 --- a/pkg/urbit/tests/jam_tests.c +++ b/pkg/urbit/tests/jam_tests.c @@ -6,7 +6,9 @@ static void _setup(void) { - u3m_boot_lite(1 << 24); + u3m_init(1 << 24); + u3m_pave(c3y); + u3e_init(); } static void diff --git a/pkg/urbit/tests/jet_tests.c b/pkg/urbit/tests/jet_tests.c index d88d55e93..4904a959d 100644 --- a/pkg/urbit/tests/jet_tests.c +++ b/pkg/urbit/tests/jet_tests.c @@ -5,7 +5,8 @@ static void _setup(void) { - u3m_boot_lite(1 << 20); + u3m_init(1 << 20); + u3m_pave(c3y); } static inline c3_i diff --git a/pkg/urbit/tests/mug_tests.c b/pkg/urbit/tests/mug_tests.c index 1c9dcddbc..ffd051674 100644 --- a/pkg/urbit/tests/mug_tests.c +++ b/pkg/urbit/tests/mug_tests.c @@ -5,7 +5,8 @@ static void _setup(void) { - u3m_boot_lite(1 << 20); + u3m_init(1 << 20); + u3m_pave(c3y); } /* _test_mug(): spot check u3r_mug hashes. diff --git a/pkg/urbit/tests/newt_tests.c b/pkg/urbit/tests/newt_tests.c index 75c0a07b1..6fdbdb759 100644 --- a/pkg/urbit/tests/newt_tests.c +++ b/pkg/urbit/tests/newt_tests.c @@ -6,7 +6,8 @@ static void _setup(void) { - u3m_boot_lite(1 << 20); + u3m_init(1 << 20); + u3m_pave(c3y); } /* _newt_encode(): synchronous serialization into a single buffer, for test purposes diff --git a/pkg/urbit/tests/meme_tests.c b/pkg/urbit/tests/nock_tests.c similarity index 80% rename from pkg/urbit/tests/meme_tests.c rename to pkg/urbit/tests/nock_tests.c index 751abb985..a96ed09bd 100644 --- a/pkg/urbit/tests/meme_tests.c +++ b/pkg/urbit/tests/nock_tests.c @@ -5,7 +5,12 @@ static void _setup(void) { - u3m_boot_lite(1 << 24); + // XX at 1<<24, this succeeds on mac, but bail:exit's on linux. + // investigate possible u3n_prog corruption + // + u3m_init(1 << 25); + u3m_pave(c3y); + u3e_init(); } static u3_noun @@ -48,7 +53,7 @@ _test_nock_meme(void) } static c3_i -_test_meme(void) +_test_nock(void) { c3_i ret_i = 1; @@ -67,8 +72,8 @@ main(int argc, char* argv[]) { _setup(); - if ( !_test_meme() ) { - fprintf(stderr, "test meme: failed\r\n"); + if ( !_test_nock() ) { + fprintf(stderr, "test nock: failed\r\n"); exit(1); } @@ -76,6 +81,6 @@ main(int argc, char* argv[]) // u3m_grab(u3_none); - fprintf(stderr, "test meme: ok\r\n"); + fprintf(stderr, "test nock: ok\r\n"); return 0; } diff --git a/pkg/urbit/tests/noun_tests.c b/pkg/urbit/tests/noun_tests.c index 1f0527bd5..af54cd289 100644 --- a/pkg/urbit/tests/noun_tests.c +++ b/pkg/urbit/tests/noun_tests.c @@ -8,7 +8,8 @@ static void _setup(void) { - u3m_boot_lite(1 << 20); + u3m_init(1 << 20); + u3m_pave(c3y); } /* _test_u3r_chop: "extract bit slices from atom" diff --git a/pkg/urbit/vere/db/lmdb.c b/pkg/urbit/vere/db/lmdb.c index c95cf38c1..443f11525 100644 --- a/pkg/urbit/vere/db/lmdb.c +++ b/pkg/urbit/vere/db/lmdb.c @@ -225,112 +225,6 @@ u3_lmdb_gulf(MDB_env* env_u, c3_d* low_d, c3_d* hig_d) } } -/* u3_lmdb_walk_init(): initialize db iterator. -*/ -c3_o -u3_lmdb_walk_init(MDB_env* env_u, - u3_lmdb_walk* itr_u, - c3_d nex_d, - c3_d las_d) -{ - // XX assumes little-endian - // - MDB_val key_u = { .mv_size = sizeof(c3_d), .mv_data = &nex_d }; - MDB_val val_u; - c3_w ops_w, ret_w; - - itr_u->red_o = c3n; - itr_u->nex_d = nex_d; - itr_u->las_d = las_d; - - // create a read-only transaction. - // - ops_w = MDB_RDONLY; - if ( (ret_w = mdb_txn_begin(env_u, 0, ops_w, &itr_u->txn_u)) ) { - mdb_logerror(stderr, ret_w, "lmdb: read txn_begin fail"); - return c3n; - } - // open the database in the transaction - // - ops_w = MDB_CREATE | MDB_INTEGERKEY; - if ( (ret_w = mdb_dbi_open(itr_u->txn_u, "EVENTS", ops_w, &itr_u->mdb_u)) ) { - mdb_logerror(stderr, ret_w, "lmdb: read: dbi_open fail"); - // XX confirm - // - mdb_txn_abort(itr_u->txn_u); - return c3n; - } - - // creates a cursor to iterate over keys starting at [eve_d] - // - if ( (ret_w = mdb_cursor_open(itr_u->txn_u, itr_u->mdb_u, &itr_u->cur_u)) ) { - mdb_logerror(stderr, ret_w, "lmdb: read: cursor_open fail"); - // XX confirm - // - mdb_txn_abort(itr_u->txn_u); - return c3n; - } - - // set the cursor to the position of [eve_d] - // - ops_w = MDB_SET_KEY; - if ( (ret_w = mdb_cursor_get(itr_u->cur_u, &key_u, &val_u, ops_w)) ) { - mdb_logerror(stderr, ret_w, "lmdb: read: initial cursor_get failed"); - fprintf(stderr, " at %" PRIu64 "\r\n", nex_d); - mdb_cursor_close(itr_u->cur_u); - // XX confirm - // - mdb_txn_abort(itr_u->txn_u); - return c3n; - } - - return c3y; -} - -/* u3_lmdb_walk_next(): synchronously read next event from iterator. -*/ -c3_o -u3_lmdb_walk_next(u3_lmdb_walk* itr_u, size_t* len_i, void** buf_v) -{ - MDB_val key_u, val_u; - c3_w ret_w, ops_w; - - c3_assert( itr_u->nex_d <= itr_u->las_d ); - - ops_w = ( c3y == itr_u->red_o ) ? MDB_NEXT : MDB_GET_CURRENT; - if ( (ret_w = mdb_cursor_get(itr_u->cur_u, &key_u, &val_u, ops_w)) ) { - mdb_logerror(stderr, ret_w, "lmdb: walk error"); - return c3n; - } - - // sanity check: ensure contiguous event numbers - // - if ( *(c3_d*)key_u.mv_data != itr_u->nex_d ) { - fprintf(stderr, "lmdb: read gap: expected %" PRIu64 - ", received %" PRIu64 "\r\n", - itr_u->nex_d, - *(c3_d*)key_u.mv_data); - return c3n; - } - - *len_i = val_u.mv_size; - *buf_v = val_u.mv_data; - - itr_u->nex_d++; - itr_u->red_o = c3y; - - return c3y; -} - -/* u3_lmdb_walk_done(): close iterator. -*/ -void -u3_lmdb_walk_done(u3_lmdb_walk* itr_u) -{ - mdb_cursor_close(itr_u->cur_u); - mdb_txn_abort(itr_u->txn_u); -} - /* u3_lmdb_read(): read [len_d] events starting at [eve_d]. */ c3_o diff --git a/pkg/urbit/vere/disk.c b/pkg/urbit/vere/disk.c index 61fe9f754..eb4a27533 100644 --- a/pkg/urbit/vere/disk.c +++ b/pkg/urbit/vere/disk.c @@ -22,12 +22,6 @@ struct _cd_save { struct _u3_disk* log_u; }; -struct _u3_disk_walk { - u3_lmdb_walk itr_u; - u3_disk* log_u; - c3_o liv_o; -}; - #undef VERBOSE_DISK #undef DISK_TRACE_JAM #undef DISK_TRACE_CUE @@ -155,47 +149,34 @@ _disk_commit_start(struct _cd_save* req_u) _disk_commit_after_cb); } - -/* u3_disk_etch(): serialize an event for persistence. RETAIN [eve] +/* _disk_serialize_v1(): serialize events in format v1. */ -size_t -u3_disk_etch(u3_disk* log_u, - u3_noun eve, - c3_l mug_l, - c3_y** out_y) +static c3_w +_disk_serialize_v1(u3_fact* tac_u, c3_y** out_y) { - size_t len_i; - c3_y* dat_y; - #ifdef DISK_TRACE_JAM - u3t_event_trace("disk etch", 'B'); + u3t_event_trace("king disk jam", 'B'); #endif - // XX check version number in log_u - // XX needs api redesign to limit allocations - // { - u3_atom mat = u3qe_jam(eve); + u3_atom mat = u3qe_jam(tac_u->job); c3_w len_w = u3r_met(3, mat); - - len_i = 4 + len_w; - dat_y = c3_malloc(len_i); - - dat_y[0] = mug_l & 0xff; - dat_y[1] = (mug_l >> 8) & 0xff; - dat_y[2] = (mug_l >> 16) & 0xff; - dat_y[3] = (mug_l >> 24) & 0xff; + c3_y* dat_y = c3_malloc(4 + len_w); + dat_y[0] = tac_u->mug_l & 0xff; + dat_y[1] = (tac_u->mug_l >> 8) & 0xff; + dat_y[2] = (tac_u->mug_l >> 16) & 0xff; + dat_y[3] = (tac_u->mug_l >> 24) & 0xff; u3r_bytes(0, len_w, dat_y + 4, mat); - u3z(mat); - } - #ifdef DISK_TRACE_JAM - u3t_event_trace("disk etch", 'E'); + u3t_event_trace("king disk jam", 'E'); #endif - *out_y = dat_y; - return len_i; + u3z(mat); + + *out_y = dat_y; + return len_w + 4; + } } /* _disk_batch(): create a write batch @@ -219,8 +200,7 @@ _disk_batch(u3_disk* log_u, c3_d len_d) for ( c3_d i_d = 0ULL; i_d < len_d; ++i_d) { c3_assert( (req_u->eve_d + i_d) == tac_u->eve_d ); - req_u->siz_i[i_d] = u3_disk_etch(log_u, tac_u->job, - tac_u->mug_l, &req_u->byt_y[i_d]); + req_u->siz_i[i_d] = _disk_serialize_v1(tac_u, &req_u->byt_y[i_d]); tac_u = tac_u->nex_u; } @@ -378,41 +358,6 @@ _disk_read_done_cb(uv_timer_t* tim_u) _disk_read_close(red_u); } -/* u3_disk_sift(): parse a persisted event buffer. -*/ -c3_o -u3_disk_sift(u3_disk* log_u, - size_t len_i, - c3_y* dat_y, - c3_l* mug_l, - u3_noun* job) -{ - if ( 4 >= len_i ) { - return c3n; - } - -#ifdef DISK_TRACE_CUE - u3t_event_trace("disk sift", 'B'); -#endif - - // XX check version in log_u - // - *mug_l = dat_y[0] - ^ (dat_y[1] << 8) - ^ (dat_y[2] << 16) - ^ (dat_y[3] << 24); - - // XX u3m_soft? - // - *job = u3ke_cue(u3i_bytes(len_i - 4, dat_y + 4)); - -#ifdef DISK_TRACE_CUE - u3t_event_trace("disk sift", 'E'); -#endif - - return c3y; -} - /* _disk_read_one_cb(): lmdb read callback, invoked for each event in order */ static c3_o @@ -422,13 +367,29 @@ _disk_read_one_cb(void* ptr_v, c3_d eve_d, size_t val_i, void* val_p) u3_disk* log_u = red_u->log_u; u3_fact* tac_u; + if ( 4 >= val_i ) { + return c3n; + } + { u3_noun job; - c3_l mug_l; + c3_y* dat_y = val_p; + c3_l mug_l = dat_y[0] + ^ (dat_y[1] << 8) + ^ (dat_y[2] << 16) + ^ (dat_y[3] << 24); - if ( c3n == u3_disk_sift(log_u, val_i, (c3_y*)val_p, &mug_l, &job) ) { - return c3n; - } +#ifdef DISK_TRACE_CUE + u3t_event_trace("king disk cue", 'B'); +#endif + + // XX u3m_soft? + // + job = u3ke_cue(u3i_bytes(val_i - 4, dat_y + 4)); + +#ifdef DISK_TRACE_CUE + u3t_event_trace("king disk cue", 'E'); +#endif tac_u = u3_fact_init(eve_d, mug_l, job); } @@ -500,120 +461,6 @@ u3_disk_read(u3_disk* log_u, c3_d eve_d, c3_d len_d) uv_timer_start(&red_u->tim_u, _disk_read_start_cb, 0, 0); } -struct _cd_list { - u3_disk* log_u; - u3_noun eve; - c3_l mug_l; -}; - -/* _disk_read_list_cb(): lmdb read callback, invoked for each event in order -*/ -static c3_o -_disk_read_list_cb(void* ptr_v, c3_d eve_d, size_t val_i, void* val_p) -{ - struct _cd_list* ven_u = ptr_v; - u3_disk* log_u = ven_u->log_u; - - { - u3_noun job; - c3_l mug_l; - - if ( c3n == u3_disk_sift(log_u, val_i, (c3_y*)val_p, &mug_l, &job) ) { - return c3n; - } - - ven_u->mug_l = mug_l; - ven_u->eve = u3nc(job, ven_u->eve); - } - - return c3y; -} - -/* u3_disk_read_list(): synchronously read a cons list of events. -*/ -u3_weak -u3_disk_read_list(u3_disk* log_u, c3_d eve_d, c3_d len_d, c3_l* mug_l) -{ - struct _cd_list ven_u = { log_u, u3_nul, 0 }; - - if ( c3n == u3_lmdb_read(log_u->mdb_u, &ven_u, - eve_d, len_d, _disk_read_list_cb) ) - { - return u3_none; - } - - *mug_l = ven_u.mug_l; - return u3kb_flop(ven_u.eve); -} - -/* u3_disk_walk_init(): init iterator. -*/ -u3_disk_walk* -u3_disk_walk_init(u3_disk* log_u, - c3_d eve_d, - c3_d len_d) -{ - u3_disk_walk* wok_u = c3_malloc(sizeof(*wok_u)); - c3_d max_d = eve_d + len_d - 1; - - wok_u->log_u = log_u; - wok_u->liv_o = u3_lmdb_walk_init(log_u->mdb_u, - &wok_u->itr_u, - eve_d, - c3_min(max_d, log_u->dun_d)); - - return wok_u; -} - -/* u3_disk_walk_live(): check if live. -*/ -c3_o -u3_disk_walk_live(u3_disk_walk* wok_u) -{ - if ( wok_u->itr_u.nex_d > wok_u->itr_u.las_d ) { - wok_u->liv_o = c3n; - } - - return wok_u->liv_o; -} - -/* u3_disk_walk_step(): get next fact. -*/ -c3_o -u3_disk_walk_step(u3_disk_walk* wok_u, u3_fact* tac_u) -{ - u3_disk* log_u = wok_u->log_u; - size_t len_i; - void* buf_v; - - tac_u->eve_d = wok_u->itr_u.nex_d; - - if ( c3n == u3_lmdb_walk_next(&wok_u->itr_u, &len_i, &buf_v) ) { - fprintf(stderr, "disk: (%" PRIu64 "): read fail\r\n", tac_u->eve_d); - return wok_u->liv_o = c3n; - } - - if ( c3n == u3_disk_sift(log_u, len_i, - (c3_y*)buf_v, - &tac_u->mug_l, - &tac_u->job) ) - { - fprintf(stderr, "disk: (%" PRIu64 "): sift fail\r\n", tac_u->eve_d); - return wok_u->liv_o = c3n; - } - - return c3y; -} - -/* u3_disk_walk_done(): close iterator. -*/ -void -u3_disk_walk_done(u3_disk_walk* wok_u) -{ - u3_lmdb_walk_done(&wok_u->itr_u); - c3_free(wok_u); -} - /* _disk_save_meta(): serialize atom, save as metadata at [key_c]. */ static c3_o diff --git a/pkg/urbit/vere/io/http.c b/pkg/urbit/vere/io/http.c index 0336cdca2..fa7d778f2 100644 --- a/pkg/urbit/vere/io/http.c +++ b/pkg/urbit/vere/io/http.c @@ -1504,61 +1504,6 @@ _http_serv_init_h2o(SSL_CTX* tls_u, c3_o log, c3_o red) return h2o_u; } -/* _http_serv_start_err(): handle errors in starting http server. -*/ -static c3_i -_http_serv_start_err(const c3_c* cap_c, u3_http* htp_u, c3_i sas_i) -{ - u3_pier* pir_u = htp_u->htd_u->car_u.pir_u; - - if ( UV_EADDRNOTAVAIL == sas_i ) { - u3l_log("http: unavailable ip address %s\r\n", u3_Host.ops_u.bin_c); - u3_king_bail(); - return 0; - } - - // ports specified, no incrementing/retry - // - if ( c3y == htp_u->dis ) { - u3l_log("http: %s (%" PRIu16 "): %s\n", - cap_c, htp_u->por_s, uv_strerror(sas_i)); - u3_king_bail(); - return 0; - } - - // increment and retry - // - if ( (UV_EADDRINUSE == sas_i) || (UV_EACCES == sas_i) ) { - if ( (c3y == htp_u->sec) && (443 == htp_u->por_s) ) { - htp_u->por_s = 8443; - } - else if ( (c3n == htp_u->sec) && (80 == htp_u->por_s) ) { - htp_u->por_s = 8080; - } - else { - htp_u->por_s++; - // XX - // - if ( c3n == htp_u->lop ) { - if ( c3y == htp_u->sec ) { - pir_u->pes_s = htp_u->por_s; - } - else { - pir_u->per_s = htp_u->por_s; - } - } - } - - return 1; - } - - // total failure XX bail? - // - u3l_log("http: %s: %s\n", cap_c, uv_strerror(sas_i)); - _http_serv_free(htp_u); - return 0; -} - /* _http_serv_start(): start http server. */ static void @@ -1574,12 +1519,10 @@ _http_serv_start(u3_http* htp_u) INADDR_ANY; if ( 0 != u3_Host.ops_u.bin_c && c3n == htp_u->lop ) { - // already validated in arguments parser - // inet_pton(AF_INET, u3_Host.ops_u.bin_c, &adr_u.sin_addr); } - c3_assert( !uv_tcp_init(u3L, &htp_u->wax_u) ); + uv_tcp_init(u3L, &htp_u->wax_u); /* Try ascending ports. */ @@ -1589,25 +1532,46 @@ _http_serv_start(u3_http* htp_u) adr_u.sin_port = htons(htp_u->por_s); if ( 0 != (sas_i = uv_tcp_bind(&htp_u->wax_u, - (const struct sockaddr*)&adr_u, 0)) ) - { - if ( _http_serv_start_err("bind", htp_u, sas_i) ) { - continue; + (const struct sockaddr*)&adr_u, 0)) || + 0 != (sas_i = uv_listen((uv_stream_t*)&htp_u->wax_u, + TCP_BACKLOG, _http_serv_listen_cb)) ) { + if ( UV_EADDRNOTAVAIL == sas_i ) { + u3l_log("http: ip address not available\n"); + u3_king_bail(); } - else { - return; + if ( c3y == htp_u->dis ) { + u3l_log("http: listen (%" PRIu16 "): %s\n", htp_u->por_s, + uv_strerror(sas_i)); + u3_king_bail(); } - } + if ( (UV_EADDRINUSE == sas_i) || (UV_EACCES == sas_i) ) { + if ( (c3y == htp_u->sec) && (443 == htp_u->por_s) ) { + htp_u->por_s = 8443; + } + else if ( (c3n == htp_u->sec) && (80 == htp_u->por_s) ) { + htp_u->por_s = 8080; + } + else { + htp_u->por_s++; + // XX + // + if ( c3n == htp_u->lop ) { + if ( c3y == htp_u->sec ) { + pir_u->pes_s = htp_u->por_s; + } + else { + pir_u->per_s = htp_u->por_s; + } + } + } - if ( 0 != (sas_i = uv_listen((uv_stream_t*)&htp_u->wax_u, - TCP_BACKLOG, _http_serv_listen_cb)) ) - { - if ( _http_serv_start_err("listen", htp_u, sas_i) ) { continue; } - else { - return; - } + + u3l_log("http: listen: %s\n", uv_strerror(sas_i)); + + _http_serv_free(htp_u); + return; } u3l_log("http: %s live on %s://localhost:%d\n", @@ -1713,30 +1677,24 @@ _http_init_tls(uv_buf_t key_u, uv_buf_t cer_u) static void _http_write_ports_file(u3_httd* htd_u, c3_c *pax_c) { + c3_c* nam_c = ".http.ports"; + c3_w len_w = 1 + strlen(pax_c) + 1 + strlen(nam_c); + + c3_c* paf_c = c3_malloc(len_w); + snprintf(paf_c, len_w, "%s/%s", pax_c, nam_c); + + c3_i por_i = c3_open(paf_c, O_WRONLY | O_CREAT | O_TRUNC, 0666); + c3_free(paf_c); + u3_http* htp_u = htd_u->htp_u; u3_pier* pir_u = htd_u->car_u.pir_u; - size_t off_i = 0; - c3_i por_i; - - { - c3_c* nam_c = ".http.ports"; - c3_w len_w = 1 + strlen(pax_c) + 1 + strlen(nam_c); - c3_c* paf_c = c3_malloc(len_w); - snprintf(paf_c, len_w, "%s/%s", pax_c, nam_c); - por_i = c3_open(paf_c, O_WRONLY | O_CREAT | O_TRUNC, 0666); - c3_free(paf_c); - } + c3_c temp[32]; while ( 0 != htp_u ) { if ( 0 < htp_u->por_s ) { - c3_c tmp_c[32]; - c3_i len_i = snprintf(tmp_c, 32, "%u %s %s\n", - htp_u->por_s, - (c3y == htp_u->sec) ? "secure" : "insecure", - (c3y == htp_u->lop) ? "loopback" : "public"); - c3_assert( 0 < len_i); - c3_assert( c3_pwrite(por_i, tmp_c, len_i, off_i) == len_i ); - off_i += len_i; + u3_write_fd(por_i, temp, snprintf(temp, 32, "%u %s %s\n", htp_u->por_s, + (c3y == htp_u->sec) ? "secure" : "insecure", + (c3y == htp_u->lop) ? "loopback" : "public")); } htp_u = htp_u->nex_u; @@ -1752,12 +1710,13 @@ static void _http_release_ports_file(c3_c *pax_c) { c3_c* nam_c = ".http.ports"; - c3_w len_w = 1 + strlen(pax_c) + 1 + strlen(nam_c); + c3_w len_w = 1 + strlen(pax_c) + 1 + strlen(nam_c); c3_c* paf_c = c3_malloc(len_w); c3_i wit_i; wit_i = snprintf(paf_c, len_w, "%s/%s", pax_c, nam_c); - c3_assert( len_w == (c3_w)wit_i + 1 ); + c3_assert(wit_i > 0); + c3_assert(len_w == (c3_w)wit_i + 1); c3_unlink(paf_c); c3_free(paf_c); diff --git a/pkg/urbit/vere/io/term.c b/pkg/urbit/vere/io/term.c index 22b4cce1e..9994fcfcb 100644 --- a/pkg/urbit/vere/io/term.c +++ b/pkg/urbit/vere/io/term.c @@ -17,6 +17,48 @@ static void _term_read_cb(uv_stream_t* tcp_u, ssize_t siz_i, const uv_buf_t* buf_u); +/* u3_write_fd(): retry interrupts, continue partial writes, assert errors. +*/ +void +u3_write_fd(c3_i fid_i, const void* buf_v, size_t len_i) +{ + ssize_t ret_i; + + while ( len_i > 0 ) { + c3_w lop_w = 0; + // retry interrupt/async errors + // + do { + // abort pathological retry loop + // + if ( 100 == ++lop_w ) { + fprintf(stderr, "term: write loop: %s\r\n", strerror(errno)); + return; + } + ret_i = write(fid_i, buf_v, len_i); + } + while ( (ret_i < 0) + && ( (errno == EINTR) + || (errno == EAGAIN) + || (errno == EWOULDBLOCK) )); + + // assert on true errors + // + // NB: can't call u3l_log here or we would re-enter u3_write_fd() + // + if ( ret_i < 0 ) { + fprintf(stderr, "term: write failed %s\r\n", strerror(errno)); + c3_assert(0); + } + // continue partial writes + // + else { + len_i -= ret_i; + buf_v += ret_i; + } + } +} + /* _term_msc_out_host(): unix microseconds from current host time. */ static c3_d @@ -213,7 +255,7 @@ u3_term_log_exit(void) if ( c3n == uty_u->sto_f(uty_u) ) { c3_assert(!"exit-tcsetattr"); } - c3_assert(c3_write(uty_u->fid_i, "\r\n", 2) == 2); + u3_write_fd(uty_u->fid_i, "\r\n", 2); } } @@ -822,7 +864,7 @@ _term_spin_step(u3_utty* uty_u) c3_w i_w; for ( i_w = bac_w; i_w < sol_w; i_w++ ) { - if ( c3_write(fid_i, lef_u.base, lef_u.len) < 0 ) { + if ( lef_u.len != write(fid_i, lef_u.base, lef_u.len) ) { return; } } @@ -832,7 +874,7 @@ _term_spin_step(u3_utty* uty_u) { c3_w len_w = cur_c - buf_c; - if ( c3_write(fid_i, buf_c, len_w) < 0 ) { + if ( len_w != write(fid_i, buf_c, len_w) ) { return; } } @@ -840,7 +882,7 @@ _term_spin_step(u3_utty* uty_u) // Cursor stays on spinner. // while ( sol_w-- ) { - if ( c3_write(fid_i, lef_u.base, lef_u.len) < 0 ) { + if ( lef_u.len != write(fid_i, lef_u.base, lef_u.len) ) { return; } } @@ -1002,9 +1044,7 @@ u3_term_ef_ctlc(void) _term_ovum_plan(uty_u->car_u, wir, cad); } - if ( c3n == u3_Host.ops_u.tem ) { - _term_it_refresh_line(uty_u); - } + _term_it_refresh_line(uty_u); } /* _term_it_put_value(): put numeric color value on lin_w. @@ -1342,10 +1382,10 @@ u3_term_io_hija(void) if ( c3y != uty_u->hij_f(uty_u) ) { c3_assert(!"hija-tcsetattr"); } - c3_assert(c3_write(uty_u->fid_i, "\r", 1) == 1); + u3_write_fd(uty_u->fid_i, "\r", 1); { uv_buf_t* buf_u = &uty_u->ufo_u.out.el_u; - c3_assert(c3_write(uty_u->fid_i, buf_u->base, buf_u->len) == buf_u->len); + u3_write_fd(uty_u->fid_i, buf_u->base, buf_u->len); } } } diff --git a/pkg/urbit/vere/io/unix.c b/pkg/urbit/vere/io/unix.c index ba0145d71..f9e175b5d 100644 --- a/pkg/urbit/vere/io/unix.c +++ b/pkg/urbit/vere/io/unix.c @@ -342,11 +342,11 @@ u3_unix_save(c3_c* pax_c, u3_atom pad) pad_y = c3_malloc(fln_w); u3r_bytes(0, fln_w, pad_y, pad); u3z(pad); - ssize_t rit_i = c3_pwrite(fid_i, pad_y, fln_w, 0); + rit_w = write(fid_i, pad_y, fln_w); close(fid_i); c3_free(pad_y); - if ( rit_i < 0 ) { + if ( rit_w != fln_w ) { u3l_log("%s: %s\n", ful_c, strerror(errno)); c3_free(ful_c); u3m_bail(c3__fail); @@ -427,7 +427,7 @@ static c3_w _unix_write_file_hard(c3_c* pax_c, u3_noun mim) { c3_i fid_i = c3_open(pax_c, O_WRONLY | O_CREAT | O_TRUNC, 0666); - c3_w len_w, siz_w, mug_w = 0; + c3_w len_w, rit_w, siz_w, mug_w = 0; c3_y* dat_y; u3_noun dat = u3t(u3t(mim)); @@ -446,10 +446,11 @@ _unix_write_file_hard(c3_c* pax_c, u3_noun mim) u3r_bytes(0, len_w, dat_y, dat); u3z(mim); - ssize_t rit_i = c3_pwrite(fid_i, dat_y, siz_w, 0); + rit_w = write(fid_i, dat_y, siz_w); - if ( rit_i < 0 ) { - u3l_log("error writing %s: %s\r\n", pax_c, strerror(errno)); + if ( rit_w != siz_w ) { + u3l_log("error writing %s: %s\r\n", + pax_c, strerror(errno)); mug_w = 0; } else { @@ -469,7 +470,7 @@ _unix_write_file_soft(u3_ufil* fil_u, u3_noun mim) { struct stat buf_u; c3_i fid_i = c3_open(fil_u->pax_c, O_RDONLY, 0644); - c3_ws len_ws; + c3_ws len_ws, red_ws; c3_w old_w; c3_y* old_y; @@ -488,21 +489,21 @@ _unix_write_file_soft(u3_ufil* fil_u, u3_noun mim) len_ws = buf_u.st_size; old_y = c3_malloc(len_ws); - ssize_t red_i = c3_pread(fid_i, old_y, len_ws, 0); + red_ws = read(fid_i, old_y, len_ws); if ( close(fid_i) < 0 ) { u3l_log("error closing file (soft) %s: %s\r\n", fil_u->pax_c, strerror(errno)); } - if ( red_i != len_ws ) { - if ( red_i < 0 ) { + if ( len_ws != red_ws ) { + if ( red_ws < 0 ) { u3l_log("error reading file (soft) %s: %s\r\n", - fil_u->pax_c, strerror(-red_i)); + fil_u->pax_c, strerror(errno)); } else { - u3l_log("wrong # of bytes read in file %s: %u %zu\r\n", - fil_u->pax_c, len_ws, red_i); + u3l_log("wrong # of bytes read in file %s: %d %d\r\n", + fil_u->pax_c, len_ws, red_ws); } c3_free(old_y); u3z(mim); @@ -892,7 +893,7 @@ _unix_update_file(u3_unix* unx_u, u3_ufil* fil_u) struct stat buf_u; c3_i fid_i = c3_open(fil_u->pax_c, O_RDONLY, 0644); - c3_ws len_ws; + c3_ws len_ws, red_ws; c3_y* dat_y; if ( fid_i < 0 || fstat(fid_i, &buf_u) < 0 ) { @@ -909,21 +910,21 @@ _unix_update_file(u3_unix* unx_u, u3_ufil* fil_u) len_ws = buf_u.st_size; dat_y = c3_malloc(len_ws); - ssize_t red_i = c3_pread(fid_i, dat_y, len_ws, 0); + red_ws = read(fid_i, dat_y, len_ws); if ( close(fid_i) < 0 ) { u3l_log("error closing file %s: %s\r\n", fil_u->pax_c, strerror(errno)); } - if ( red_i != len_ws ) { - if ( red_i < 0 ) { + if ( len_ws != red_ws ) { + if ( red_ws < 0 ) { u3l_log("error reading file %s: %s\r\n", - fil_u->pax_c, strerror(-red_i)); + fil_u->pax_c, strerror(errno)); } else { - u3l_log("wrong # of bytes read in file %s: %u %zu\r\n", - fil_u->pax_c, len_ws, red_i); + u3l_log("wrong # of bytes read in file %s: %d %d\r\n", + fil_u->pax_c, len_ws, red_ws); } c3_free(dat_y); return u3_nul; @@ -1158,7 +1159,7 @@ _unix_initial_update_file(c3_c* pax_c, c3_c* bas_c) { struct stat buf_u; c3_i fid_i = c3_open(pax_c, O_RDONLY, 0644); - c3_ws len_ws; + c3_ws len_ws, red_ws; c3_y* dat_y; if ( fid_i < 0 || fstat(fid_i, &buf_u) < 0 ) { @@ -1175,21 +1176,21 @@ _unix_initial_update_file(c3_c* pax_c, c3_c* bas_c) len_ws = buf_u.st_size; dat_y = c3_malloc(len_ws); - ssize_t red_i = c3_pread(fid_i, dat_y, len_ws, 0); + red_ws = read(fid_i, dat_y, len_ws); if ( close(fid_i) < 0 ) { u3l_log("error closing initial file %s: %s\r\n", pax_c, strerror(errno)); } - if ( red_i != len_ws ) { - if ( red_i < 0 ) { + if ( len_ws != red_ws ) { + if ( red_ws < 0 ) { u3l_log("error reading initial file %s: %s\r\n", - pax_c, strerror(-red_i)); + pax_c, strerror(errno)); } else { - u3l_log("wrong # of bytes read in initial file %s: %u %zu\r\n", - pax_c, len_ws, red_i); + u3l_log("wrong # of bytes read in initial file %s: %d %d\r\n", + pax_c, len_ws, red_ws); } c3_free(dat_y); return u3_nul; diff --git a/pkg/urbit/vere/king.c b/pkg/urbit/vere/king.c index b18d7b441..44a8cd502 100644 --- a/pkg/urbit/vere/king.c +++ b/pkg/urbit/vere/king.c @@ -307,7 +307,7 @@ _king_get_pace(void) { struct stat buf_u; c3_c* pat_c; - c3_w len_w; + c3_w red_w, len_w; c3_i ret_i, fid_i; ret_i = asprintf(&pat_c, "%s/.bin/pace", u3_Host.dir_c); @@ -324,10 +324,10 @@ _king_get_pace(void) len_w = buf_u.st_size; pat_c = c3_malloc(len_w + 1); - ssize_t red_i = c3_pread(fid_i, pat_c, len_w, 0); + red_w = read(fid_i, pat_c, len_w); close(fid_i); - if ( red_i != len_w ) { + if ( len_w != red_w ) { c3_free(pat_c); u3l_log("unable to read pace file, " "falling back to default (\"live\")\n"); @@ -1038,6 +1038,9 @@ _king_make_pace(c3_c* pac_c) return 0; } +static c3_i +_king_write_raw(c3_i fid_i, c3_y* buf_y, size_t len_i); + /* _king_init_pace(): save pace file if not present */ static c3_i @@ -1061,11 +1064,8 @@ _king_init_pace(c3_c* pac_c) } } - size_t len_i = strlen(pac_c); - ssize_t wit_i = c3_pwrite(fid_i, pac_c, len_i, 0); - if ( wit_i != len_i ) { - u3l_log("dock: init pace (%s) write failed: %s\n", - pac_c, strerror(errno)); + if ( _king_write_raw(fid_i, (c3_y*)pac_c, strlen(pac_c)) ) { + u3l_log("dock: init pace (%s): write %s\n", pac_c, strerror(errno)); close(fid_i); c3_free(bin_c); return -1; @@ -1239,24 +1239,62 @@ _king_do_upgrade(c3_c* pac_c, c3_c* ver_c) // XX print restart instructions } -static c3_i -_king_copy_raw(c3_i src_i, c3_i dst_i, c3_y* buf_y, size_t pag_i) +/* _king_read_raw: read (up to) [len_i] from [fid_i] to [buf_y] +*/ +static ssize_t +_king_read_raw(c3_i fid_i, c3_y* buf_y, size_t len_i) { - size_t off_i = 0; ssize_t ret_i; do { - if ( 0 > (ret_i = c3_pread(src_i, buf_y, pag_i, off_i)) ) { - return ret_i; - } - - if ( 0 > (ret_i = c3_pwrite(dst_i, buf_y, (size_t)ret_i, off_i)) ) { - return ret_i; - } - - off_i += (size_t)ret_i; + ret_i = read(fid_i, buf_y, len_i); } - while ( ret_i ); + while ( (ret_i < 0) && (errno == EINTR) ); + + return ret_i; +} + +/* _king_read_raw: write [len_i] from [buf_y] to [fid_i]. +*/ +static c3_i +_king_write_raw(c3_i fid_i, c3_y* buf_y, size_t len_i) +{ + ssize_t ret_i; + + while ( len_i ) { + + do { + ret_i = write(fid_i, buf_y, len_i); + } + while ( (ret_i < 0) && (errno == EINTR) ); + + if ( ret_i < 0 ) { + return -1; + } + else { + len_i -= ret_i; + buf_y += ret_i; + } + } + + return 0; +} + +static c3_i +_king_copy_raw(c3_i src_i, c3_i dst_i, c3_y* buf_y, size_t pag_i) +{ + ssize_t red_i; + + do { + if ( 0 > (red_i = _king_read_raw(src_i, buf_y, pag_i)) ) { + return -1; + } + + if ( _king_write_raw(dst_i, buf_y, (size_t)red_i) ) { + return -1; + } + } + while ( red_i ); return 0; } @@ -1299,8 +1337,6 @@ _king_copy_file(c3_c* src_c, c3_c* dst_c) goto done1; } - // XX O_TRUNC? - // if ( -1 == (dst_i = open(dst_c, O_RDWR | O_CREAT, 0755)) ) { err_i = errno; ret_i = -1; @@ -1367,12 +1403,8 @@ _king_copy_file(c3_c* src_c, c3_c* dst_c) { size_t pag_i = 1 << 14;; c3_y* buf_y = c3_malloc(pag_i); - - if ( 0 > (ret_i = _king_copy_raw(src_i, dst_i, buf_y, pag_i)) ) { - err_i = errno; - ret_i = -1; - } - + ret_i = _king_copy_raw(src_i, dst_i, buf_y, pag_i); + err_i = errno; c3_free(buf_y); } diff --git a/pkg/urbit/vere/lord.c b/pkg/urbit/vere/lord.c index d51945530..205190848 100644 --- a/pkg/urbit/vere/lord.c +++ b/pkg/urbit/vere/lord.c @@ -1051,7 +1051,7 @@ _lord_on_serf_err_cb(uv_stream_t* pyp_u, // serf used to write to 2 directly // this can't be any worse than that // - c3_assert(c3_write(STDERR_FILENO, buf_u->base, siz_i) == siz_i); + u3_write_fd(2, buf_u->base, siz_i); } else { uv_read_stop(pyp_u); diff --git a/pkg/urbit/version b/pkg/urbit/version index 64349e8f7..9f76d37b7 100644 --- a/pkg/urbit/version +++ b/pkg/urbit/version @@ -1 +1 @@ -1.14-rc3 \ No newline at end of file +1.13 \ No newline at end of file diff --git a/pkg/urbit/worker/mars.c b/pkg/urbit/worker/mars.c deleted file mode 100644 index 9e38844cc..000000000 --- a/pkg/urbit/worker/mars.c +++ /dev/null @@ -1,267 +0,0 @@ -/* worker/mars.c -** -** the main loop of a mars process. -*/ -#include "all.h" -#include -#include - -/* _mars_step_trace(): initialize or rotate trace file. -*/ -static void -_mars_step_trace(const c3_c* dir_c) -{ - if ( u3C.wag_w & u3o_trace ) { - if ( u3_Host.tra_u.con_w == 0 && u3_Host.tra_u.fun_w == 0 ) { - u3t_trace_open(dir_c); - } - else if ( u3_Host.tra_u.con_w >= 100000 ) { - u3t_trace_close(); - u3t_trace_open(dir_c); - } - } -} - -/* _mars_poke_play(): replay an event. -*/ -static u3_weak -_mars_poke_play(u3_mars* mar_u, c3_d eve_d, u3_noun job) -{ - u3_noun vir; - - if ( c3n == u3v_poke_sure(0, job, &vir) ) { - return vir; - } - - u3z(vir); - return u3_none; -} - -typedef enum { - _play_yes_e, // success - _play_mem_e, // %meme - _play_int_e, // %intr - _play_log_e, // event log fail - _play_mug_e, // mug mismatch - _play_bad_e // total failure -} _mars_play_e; - -/* _mars_play_batch(): replay a batch of events. -*/ -static _mars_play_e -_mars_play_batch(u3_mars* mar_u, c3_o mug_o, c3_w bat_w) -{ - u3_disk* log_u = mar_u->log_u; - u3_disk_walk* wok_u = u3_disk_walk_init(log_u, mar_u->dun_d + 1, bat_w); - u3_fact tac_u; - u3_noun dud; - - while ( c3y == u3_disk_walk_live(wok_u) ) { - if ( c3n == u3_disk_walk_step(wok_u, &tac_u) ) { - u3_disk_walk_done(wok_u); - return _play_log_e; - } - - c3_assert( ++mar_u->sen_d == tac_u.eve_d ); - - if ( u3_none != (dud = _mars_poke_play(mar_u, tac_u.eve_d, tac_u.job)) ) { - c3_m mot_m; - - mar_u->sen_d = mar_u->dun_d; - u3_disk_walk_done(wok_u); - - c3_assert( c3y == u3r_safe_word(u3h(dud), &mot_m) ); - - switch ( mot_m ) { - case c3__meme: { - fprintf(stderr, "play (%" PRIu64 "): %%meme\r\n", tac_u.eve_d); - u3z(dud); - return _play_mem_e; - } - - case c3__intr: { - fprintf(stderr, "play (%" PRIu64 "): %%intr\r\n", tac_u.eve_d); - u3z(dud); - return _play_int_e; - } - - default: { - fprintf(stderr, "play (%" PRIu64 "): failed\r\n", tac_u.eve_d); - u3_pier_punt_goof("play", dud); - // XX say something uplifting - // - return _play_bad_e; - } - } - } - - mar_u->mug_l = u3r_mug(u3A->roc); - - if ( tac_u.mug_l && (mar_u->mug_l != tac_u.mug_l) ) { - fprintf(stderr, "play (%" PRIu64 "): mug mismatch " - "expected %08x, actual %08x\r\n", - tac_u.eve_d, tac_u.mug_l, mar_u->mug_l); - - if ( c3y == mug_o ) { - mar_u->sen_d = mar_u->dun_d; - u3_disk_walk_done(wok_u); - return _play_mug_e; - } - } - - mar_u->dun_d = mar_u->sen_d; - } - - u3_disk_walk_done(wok_u); - - return _play_yes_e; -} - -static c3_o -_mars_do_boot(u3_disk* log_u, c3_d eve_d) -{ - u3_weak eve; - c3_l mug_l; - - if ( u3_none == (eve = u3_disk_read_list(log_u, 1, eve_d, &mug_l)) ) { - fprintf(stderr, "boot: read failed\r\n"); - return c3n; - } - - u3l_log("--------------- bootstrap starting ----------------\r\n"); - - u3l_log("boot: 1-%u\r\n", u3qb_lent(eve)); - - if ( c3n == u3v_boot(eve) ) { - return c3n; - } - - u3l_log("--------------- bootstrap complete ----------------\r\n"); - return c3y; -} - -/* u3_mars_play(): replay logged events up to [eve_d]. -*/ -void -u3_mars_play(u3_mars* mar_u, c3_d eve_d) -{ - u3_disk* log_u = mar_u->log_u; - - if ( !eve_d ) { - eve_d = log_u->dun_d; - } - else if ( eve_d <= mar_u->dun_d ) { - u3l_log("mars: already computed %" PRIu64 "\r\n", eve_d); - u3l_log(" state=%" PRIu64 ", log=%" PRIu64 "\r\n", - mar_u->dun_d, log_u->dun_d); - return; - } - else { - eve_d = c3_min(eve_d, log_u->dun_d); - } - - if ( !mar_u->dun_d ) { - c3_w lif_w; - - if ( c3n == u3_disk_read_meta(log_u, 0, 0, &lif_w) ) { - fprintf(stderr, "mars: disk read meta fail\r\n"); - // XX exit code, cb - // - exit(1); - } - - if ( c3n == _mars_do_boot(mar_u->log_u, lif_w) ) { - fprintf(stderr, "mars: boot fail\r\n"); - // XX exit code, cb - // - exit(1); - } - - mar_u->sen_d = mar_u->dun_d = lif_w; - } - - if ( mar_u->dun_d == log_u->dun_d ) { - u3l_log("mars: nothing to do!\r\n"); - return; - } - - u3l_log("---------------- playback starting ----------------\r\n"); - - if ( (1ULL + eve_d) == log_u->dun_d ) { - u3l_log("play: event %" PRIu64 "\r\n", log_u->dun_d); - } - else if ( eve_d != log_u->dun_d ) { - u3l_log("play: events %" PRIu64 "-%" PRIu64 " of %" PRIu64 "\r\n", - (c3_d)(1ULL + mar_u->dun_d), - eve_d, - log_u->dun_d); - } - else { - u3l_log("play: events %" PRIu64 "-%" PRIu64 "\r\n", - (c3_d)(1ULL + mar_u->dun_d), - eve_d); - } - - { - c3_d mem_d = 0; // last event to meme - c3_w try_w = 0; // [mem_d] retry count - - while ( mar_u->dun_d < eve_d ) { - _mars_step_trace(mar_u->dir_c); - - // XX get batch from args - // - switch ( _mars_play_batch(mar_u, c3y, 1024) ) { - case _play_yes_e: { - u3l_log("play (%" PRIu64 "): done\r\n", mar_u->dun_d); - u3m_reclaim(); - - // XX save a snapshot every N events? - // - } break; - - case _play_mem_e: { - if ( mem_d != mar_u->dun_d ) { - mem_d = mar_u->dun_d; - try_w = 0; - } - else if ( 3 == ++try_w ) { - fprintf(stderr, "play (%" PRIu64 "): failed\r\n", mar_u->dun_d + 1); - u3m_save(); - // XX check loom size, suggest --loom X - // XX exit code, cb - // - u3_disk_exit(log_u); - exit(1); - } - - // XX pack before meld? - // - if ( u3C.wag_w & u3o_auto_meld ) { - u3a_print_memory(stderr, "mars: meld: gained", u3u_meld()); - } - else { - u3a_print_memory(stderr, "mars: pack: gained", u3m_pack()); - } - } break; - - // XX handle any specifically? - // - case _play_int_e: - case _play_log_e: - case _play_mug_e: - case _play_bad_e: { - fprintf(stderr, "play (%" PRIu64 "): failed\r\n", mar_u->dun_d + 1); - u3m_save(); - // XX exit code, cb - // - u3_disk_exit(log_u); - exit(1); - } - } - } - } - - u3l_log("---------------- playback complete ----------------\r\n"); - u3m_save(); -} diff --git a/pkg/urbit/worker/serf.c b/pkg/urbit/worker/serf.c index 377d26fc0..85420a122 100644 --- a/pkg/urbit/worker/serf.c +++ b/pkg/urbit/worker/serf.c @@ -49,6 +49,137 @@ -- */ +/* _serf_space(): print n spaces. +*/ +static void +_serf_space(FILE* fil_u, c3_w n) +{ + for (; n > 0; n--) + (fprintf(fil_u," ")); +} + +/* _serf_print_memory(): print memory amount. +** +** Helper for _serf_prof(), just an un-captioned u3a_print_memory(). +*/ +static void +_serf_print_memory(FILE* fil_u, c3_w wor_w) +{ + c3_w byt_w = (wor_w * 4); + c3_w gib_w = (byt_w / 1000000000); + c3_w mib_w = (byt_w % 1000000000) / 1000000; + c3_w kib_w = (byt_w % 1000000) / 1000; + c3_w bib_w = (byt_w % 1000); + + if ( gib_w ) { + (fprintf(fil_u, "GB/%d.%03d.%03d.%03d\r\n", + gib_w, mib_w, kib_w, bib_w)); + } + else if ( mib_w ) { + (fprintf(fil_u, "MB/%d.%03d.%03d\r\n", mib_w, kib_w, bib_w)); + } + else if ( kib_w ) { + (fprintf(fil_u, "KB/%d.%03d\r\n", kib_w, bib_w)); + } + else { + (fprintf(fil_u, "B/%d\r\n", bib_w)); + } +} + +/* _serf_prof(): print memory profile. RETAIN. +*/ +c3_w +_serf_prof(FILE* fil_u, c3_w den, u3_noun mas) +{ + c3_w tot_w = 0; + u3_noun h_mas, t_mas; + + if ( c3n == u3r_cell(mas, &h_mas, &t_mas) ) { + _serf_space(fil_u, den); + fprintf(fil_u, "mistyped mass\r\n"); + return tot_w; + } + else if ( _(u3du(h_mas)) ) { + _serf_space(fil_u, den); + fprintf(fil_u, "mistyped mass head\r\n"); + { + c3_c* lab_c = u3m_pretty(h_mas); + fprintf(fil_u, "h_mas: %s", lab_c); + c3_free(lab_c); + } + return tot_w; + } + else { + _serf_space(fil_u, den); + + { + c3_c* lab_c = u3m_pretty(h_mas); + fprintf(fil_u, "%s: ", lab_c); + c3_free(lab_c); + } + + u3_noun it_mas, tt_mas; + + if ( c3n == u3r_cell(t_mas, &it_mas, &tt_mas) ) { + fprintf(fil_u, "mistyped mass tail\r\n"); + return tot_w; + } + else if ( c3y == it_mas ) { + tot_w += u3a_mark_noun(tt_mas); + _serf_print_memory(fil_u, tot_w); + +#if 1 + /* The basic issue here is that tt_mas is included in .sac + * (the whole profile), so they can't both be roots in the + * normal sense. When we mark .sac later on, we want tt_mas + * to appear unmarked, but its children should be already + * marked. + */ + if ( _(u3a_is_dog(tt_mas)) ) { + u3a_box* box_u = u3a_botox(u3a_to_ptr(tt_mas)); +#ifdef U3_MEMORY_DEBUG + if ( 1 == box_u->eus_w ) { + box_u->eus_w = 0xffffffff; + } + else { + box_u->eus_w -= 1; + } +#else + if ( -1 == (c3_w)box_u->use_w ) { + box_u->use_w = 0x80000000; + } + else { + box_u->use_w += 1; + } +#endif + } +#endif + + return tot_w; + } + else if ( c3n == it_mas ) { + fprintf(fil_u, "\r\n"); + + while ( _(u3du(tt_mas)) ) { + tot_w += _serf_prof(fil_u, den+2, u3h(tt_mas)); + tt_mas = u3t(tt_mas); + } + + _serf_space(fil_u, den); + fprintf(fil_u, "--"); + _serf_print_memory(fil_u, tot_w); + + return tot_w; + + } + else { + _serf_space(fil_u, den); + fprintf(fil_u, "mistyped (strange) mass tail\r\n"); + return tot_w; + } + } +} + /* _serf_grab(): garbage collect, checking for profiling. RETAIN. */ static void @@ -69,7 +200,7 @@ _serf_grab(u3_noun sac) c3_c* wen_c = u3r_string(wen); c3_c nam_c[2048]; - snprintf(nam_c, 2048, "%s/.urb/put/mass", u3C.dir_c); + snprintf(nam_c, 2048, "%s/.urb/put/mass", u3P.dir_c); struct stat st; if ( -1 == stat(nam_c, &st) ) { @@ -94,7 +225,7 @@ _serf_grab(u3_noun sac) c3_assert( u3R == &(u3H->rod_u) ); fprintf(fil_u, "\r\n"); - tot_w += u3a_maid(fil_u, "total userspace", u3a_prof(fil_u, 0, sac)); + tot_w += u3a_maid(fil_u, "total userspace", _serf_prof(fil_u, 0, sac)); tot_w += u3m_mark(fil_u); tot_w += u3a_maid(fil_u, "space profile", u3a_mark_noun(sac)); @@ -708,7 +839,7 @@ _serf_writ_live_exit(u3_serf* sef_u, c3_w cod_w) c3_c* wen_c = u3r_string(wen); c3_c nam_c[2048]; - snprintf(nam_c, 2048, "%s/.urb/put/profile", u3C.dir_c); + snprintf(nam_c, 2048, "%s/.urb/put/profile", u3P.dir_c); struct stat st; if ( -1 == stat(nam_c, &st) ) { @@ -752,7 +883,7 @@ _serf_writ_live_save(u3_serf* sef_u, c3_d eve_d) exit(1); } - u3m_save(); + u3e_save(); } /* u3_serf_live(): apply %live command [com], producing *ret on c3y. @@ -823,7 +954,7 @@ u3_serf_live(u3_serf* sef_u, u3_noun com, u3_noun* ret) return c3n; } - u3m_save(); + u3e_save(); u3_serf_grab(); *ret = u3nc(c3__live, u3_nul); @@ -850,7 +981,7 @@ u3_serf_live(u3_serf* sef_u, u3_noun com, u3_noun* ret) } else { u3z(com); - u3a_print_memory(stderr, "serf: meld: gained", u3u_meld()); + u3u_meld(); *ret = u3nc(c3__live, u3_nul); return c3y; } From 2f17a17406e2b9cfd40f3ce82fa793e27f041d16 Mon Sep 17 00:00:00 2001 From: Philip Monk Date: Tue, 3 Jan 2023 11:37:43 -0700 Subject: [PATCH 129/136] Revert "wip" This reverts commit 0e16d82a46ea569e04326ba497b99f93b7ddc751. --- .husky/post-checkout | 3 --- .husky/post-commit | 3 --- .husky/post-merge | 3 --- .husky/pre-push | 3 --- pkg/urbit/noun/manage.c | 6 ------ pkg/urbit/vere/io/fore.c | 7 ++----- 6 files changed, 2 insertions(+), 23 deletions(-) delete mode 100755 .husky/post-checkout delete mode 100755 .husky/post-commit delete mode 100755 .husky/post-merge delete mode 100755 .husky/pre-push diff --git a/.husky/post-checkout b/.husky/post-checkout deleted file mode 100755 index cab40f264..000000000 --- a/.husky/post-checkout +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-checkout.\n"; exit 2; } -git lfs post-checkout "$@" diff --git a/.husky/post-commit b/.husky/post-commit deleted file mode 100755 index 9443f4161..000000000 --- a/.husky/post-commit +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-commit.\n"; exit 2; } -git lfs post-commit "$@" diff --git a/.husky/post-merge b/.husky/post-merge deleted file mode 100755 index 828b70891..000000000 --- a/.husky/post-merge +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-merge.\n"; exit 2; } -git lfs post-merge "$@" diff --git a/.husky/pre-push b/.husky/pre-push deleted file mode 100755 index 81a9cc639..000000000 --- a/.husky/pre-push +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/pre-push.\n"; exit 2; } -git lfs pre-push "$@" diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 33b57a43a..aa33b0328 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -433,7 +433,6 @@ u3m_signal(u3_noun sig_l) u3_noun u3m_file(c3_c* pas_c) { - u3l_log("Loading file: %s\r\n", pas_c); struct stat buf_b; c3_i fid_i = c3_open(pas_c, O_RDONLY, 0644); c3_w fln_w, red_w; @@ -444,8 +443,6 @@ u3m_file(c3_c* pas_c) return u3m_bail(c3__fail); } fln_w = buf_b.st_size; - //u3l_log("file size: %i\r\n", fln_w); - pad_y = c3_malloc(buf_b.st_size); red_w = read(fid_i, pad_y, fln_w); @@ -459,9 +456,6 @@ u3m_file(c3_c* pas_c) u3_noun pad = u3i_bytes(fln_w, (c3_y *)pad_y); c3_free(pad_y); - //u3l_log("size of noun\r\n"); - //u3m_p("size", u3dc("met", 3, u3k(pad))); - return pad; } } diff --git a/pkg/urbit/vere/io/fore.c b/pkg/urbit/vere/io/fore.c index ac3240d6f..7354a8225 100644 --- a/pkg/urbit/vere/io/fore.c +++ b/pkg/urbit/vere/io/fore.c @@ -78,12 +78,9 @@ _fore_inject(u3_auto* car_u, c3_c* pax_c) static void _fore_import(u3_auto* car_u, c3_c* pax_c) { - u3_noun fil = u3m_file(pax_c); - //u3m_p("imp file size", u3dc("met", 3, fil)); - u3_noun imp = u3dt("cat", 3, u3i_string("#import_"), fil); + u3_noun arc = u3ke_cue(u3m_file(pax_c)); + u3_noun imp = u3dt("cat", 3, u3i_string("#import_"), arc); u3_noun siz = u3r_met(3, imp); - u3m_p("imp cue size", siz); - u3_noun dat = u3nt(u3_nul, siz, imp); u3_noun req = u3nt(c3n, From 4e3698f327c6749e5be750d28f2a91ddec598487 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Tue, 3 Jan 2023 17:47:26 -0600 Subject: [PATCH 130/136] group-store: instead of only watching missing, restart any migration subscriptions that might be bad --- pkg/landscape/app/group-store.hoon | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 16e66fe35..2eb5f3d9c 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -242,19 +242,22 @@ =. wait put-missing =^ cards state - watch-missing + rewatch [cards state] :: - ++ watch-missing + ++ rewatch =/ wait ~(tap in wait) =| cards=(list card) |- ?~ wait [cards state] - ?: (~(has by wex.bol) [/gladio/(scot %p i.wait) i.wait dap.bol]) - $(wait t.wait) + =/ wir /gladio/(scot %p i.wait) =. cards :_(cards (watch-init-migrate i.wait)) + :: if we have a subscription already, leave first to restart + =? cards + (~(has by wex.bol) [wir i.wait dap.bol]) + :_(cards [%pass wir %agent [our.bol dap.bol] %leave ~]) $(wait t.wait) :: ++ put-missing From 264678a908fa4e913aad28259ee5eff47e6f39a4 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Tue, 3 Jan 2023 18:17:02 -0600 Subject: [PATCH 131/136] group-store: correct target --- pkg/landscape/app/group-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 2eb5f3d9c..b41645948 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -257,7 +257,7 @@ :: if we have a subscription already, leave first to restart =? cards (~(has by wex.bol) [wir i.wait dap.bol]) - :_(cards [%pass wir %agent [our.bol dap.bol] %leave ~]) + :_(cards [%pass wir %agent [i.wait %groups] %leave ~]) $(wait t.wait) :: ++ put-missing From 936835cd0c2cba5f513c888456d4244b5eb6dc47 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Tue, 3 Jan 2023 18:50:05 -0600 Subject: [PATCH 132/136] groups-store: fixing wire check for leaves --- pkg/landscape/app/group-store.hoon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index b41645948..5efa484c6 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -256,8 +256,8 @@ :_(cards (watch-init-migrate i.wait)) :: if we have a subscription already, leave first to restart =? cards - (~(has by wex.bol) [wir i.wait dap.bol]) - :_(cards [%pass wir %agent [i.wait %groups] %leave ~]) + (~(has by wex.bol) [wir i.wait %groups]) + :_(cards [%pass wir %agent [i.wait %groups] %leave ~]) $(wait t.wait) :: ++ put-missing From 563aed6b7f0c8eba06174885b275d0054158b551 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 4 Jan 2023 11:17:44 -0600 Subject: [PATCH 133/136] group-store: automatically run rebuild on load --- pkg/landscape/app/group-store.hoon | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 5efa484c6..6b3bfc9f6 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -84,7 +84,10 @@ =| cards=(list card) |^ ?- -.old - %3 [(flop cards) this(state old)] + %3 + :_ this(state old) + :_ cards + [%pass /pyre/rebuild %agent [our dap]:bowl %poke noun+!>(%rebuild)] :: %2 %_ $ From 9316aebec75558203d065f2c3576dbfafe1776d2 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 4 Jan 2023 12:40:18 -0600 Subject: [PATCH 134/136] group-store: only run rebuild once --- pkg/landscape/app/group-store.hoon | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/pkg/landscape/app/group-store.hoon b/pkg/landscape/app/group-store.hoon index 6b3bfc9f6..bd4dcf982 100644 --- a/pkg/landscape/app/group-store.hoon +++ b/pkg/landscape/app/group-store.hoon @@ -41,6 +41,7 @@ state-one state-two state-three + state-four == :: +$ state-zero @@ -61,9 +62,15 @@ =groups wait=(set ship) == +:: ++$ state-four + $: %4 + =groups + wait=(set ship) + == -- :: -=| state-three +=| state-four =* state - :: %- agent:dbug @@ -84,10 +91,15 @@ =| cards=(list card) |^ ?- -.old - %3 - :_ this(state old) - :_ cards - [%pass /pyre/rebuild %agent [our dap]:bowl %poke noun+!>(%rebuild)] + %4 [(flop cards) this(state old)] + :: + %3 + %_ $ + old [%4 +.old] + cards + :_ cards + [%pass /pyre/rebuild %agent [our dap]:bowl %poke noun+!>(%rebuild)] + == :: %2 %_ $ @@ -391,8 +403,8 @@ |= arc=* ^- (quip card _state) |^ - =/ sty=state-three - [%3 (remake-groups ;;((tree [resource tree-group]) +.arc)) ~] + =/ sty=state-four + [%4 (remake-groups ;;((tree [resource tree-group]) +.arc)) ~] :_ sty %+ roll ~(tap by groups.sty) |= [[rid=resource grp=group] out=(list card)] From 00f9908cb6fe616bbf6f510b00bc613d5e2926b5 Mon Sep 17 00:00:00 2001 From: fang Date: Thu, 5 Jan 2023 18:53:44 +0100 Subject: [PATCH 135/136] ci: remove the mingw build flow As discussed internally at the end of last year, we are discontinuing Windows support for vere. The tldr is that we presently don't have the resources to support non-*nix systems at the level of quality we want to deliver. Notably Windows is already very much a second-class citizen (#5822 and others). Windows users may want to run Urbit through WSL instead. As such, the Window build step should be removed from our CI. --- .github/workflows/vere.yml | 79 +------------------------------------- 1 file changed, 1 insertion(+), 78 deletions(-) diff --git a/.github/workflows/vere.yml b/.github/workflows/vere.yml index eb94e8ea9..9315d8bf4 100644 --- a/.github/workflows/vere.yml +++ b/.github/workflows/vere.yml @@ -162,86 +162,9 @@ jobs: - if: ${{ matrix.os == 'ubuntu-latest' }} run: nix-build -A docker-image - mingw: - runs-on: windows-latest - defaults: - run: - shell: > - C:\msys64\msys2_shell.cmd -mingw64 -defterm -no-start -here -c - ". <(cygpath '{0}')" - working-directory: ./pkg/urbit - - steps: - - uses: actions/checkout@v2 - with: - lfs: true - - # echo suppresses pacman prompt - - run: echo|./configure - env: - CACHIX_CACHE: ares - CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} - - - run: mingw32-make build/urbit - - run: mingw32-make test - - run: > - build/urbit -l -d -B ../../bin/solid.pill -F bus && - curl -f --data '{"source":{"dojo":"+hood/exit"},"sink":{"app":"hood"}}' - http://localhost:12321 - - - name: confirm binary is mostly static - run: | - if [ -z "$(ldd build/urbit | grep -vi "windows/system32")"]; then - echo "it's mostly static" - exit 0 - else - echo "dynamic links found:" - ldd build/urbit - exit 1 - fi - - - uses: actions/setup-python@v2 - if: inputs.upload - with: - python-version: 3.7 - - - uses: google-github-actions/setup-gcloud@v0.6.0 - if: inputs.upload - env: - # see https://github.com/google-github-actions/setup-gcloud/issues/100 - CLOUDSDK_PYTHON: ${{env.pythonLocation}}\python.exe - with: - service_account_key: ${{ secrets.GCS_SERVICE_ACCOUNT_KEY }} - project_id: ${{ secrets.GCS_PROJECT }} - export_default_credentials: true - - - name: upload binary to bootstrap.urbit.org - if: inputs.upload - env: - CLOUDSDK_PYTHON: ${{env.pythonLocation}}\python.exe - shell: bash - run: | - if [ "real" == "$VERSION_TYPE" ]; then - version="$(cat ./version)" - else - version="${GITHUB_SHA:0:9}" - fi - - system="x86_64-windows" - target="gs://${UPLOAD_BASE}/${VERE_PACE}/${version}/vere-v${version}-${system}.exe" - - gsutil cp -n ./build/urbit.exe "$target" - exitcode=$? - - test $exitcode -eq 0 && - echo "upload to $target complete." || - echo "upload to $target failed."; - exit $exitcode - - after: runs-on: ubuntu-latest - needs: [urbit, mingw] + needs: [urbit] if: inputs.upload steps: - uses: google-github-actions/setup-gcloud@v0.2.0 From b1a5e109f56fbe6eb0cf55cc63bd96360f75d191 Mon Sep 17 00:00:00 2001 From: fang Date: Thu, 5 Jan 2023 18:56:46 +0100 Subject: [PATCH 136/136] ci: remove frontend-tests flow These are no longer being used as much as they were, most of the stuff it was hitting lives in separate repos now. Can be ripped out as such. --- .github/workflows/frontend-test.yml | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 .github/workflows/frontend-test.yml diff --git a/.github/workflows/frontend-test.yml b/.github/workflows/frontend-test.yml deleted file mode 100644 index 89e68662a..000000000 --- a/.github/workflows/frontend-test.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: frontend-test - -on: - pull_request: - paths: - - 'pkg/interface/**' - - 'pkg/btc-wallet/**' - - 'pkg/npm/**' - -jobs: - frontend-test: - runs-on: ubuntu-latest - name: "Test changed frontend packages" - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - run: git fetch --prune - - name: 'Setup root deps' - run: npm ci - - name: 'Setup dependencies' - run: npm run bootstrap - - name: 'Run tests' - run: npm run test -- --since origin/$GITHUB_BASE_REF --include-dependents