cmd/hakurei: short identifier from lower half
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Test / Create distribution (push) Successful in 33s
				
			
		
			
				
	
				Test / Sandbox (push) Successful in 39s
				
			
		
			
				
	
				Test / Sandbox (race detector) (push) Successful in 40s
				
			
		
			
				
	
				Test / Hakurei (push) Successful in 2m14s
				
			
		
			
				
	
				Test / Hakurei (race detector) (push) Successful in 2m57s
				
			
		
			
				
	
				Test / Hpkg (push) Successful in 3m12s
				
			
		
			
				
	
				Test / Flake checks (push) Successful in 1m25s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 33s
				
			Test / Sandbox (push) Successful in 39s
				
			Test / Sandbox (race detector) (push) Successful in 40s
				
			Test / Hakurei (push) Successful in 2m14s
				
			Test / Hakurei (race detector) (push) Successful in 2m57s
				
			Test / Hpkg (push) Successful in 3m12s
				
			Test / Flake checks (push) Successful in 1m25s
				
			The upper half is now a nanosecond timestamp. Lower half is still random bytes, so use lower half for short identifier. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
		
							parent
							
								
									2442eda8d9
								
							
						
					
					
						commit
						7de593e816
					
				| @ -301,7 +301,7 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr | ||||
| 
 | ||||
| 			case 1: // instance | ||||
| 				name := args[0] | ||||
| 				config, entry := tryShort(msg, name) | ||||
| 				config, entry := tryIdentifier(msg, name) | ||||
| 				if config == nil { | ||||
| 					config = tryPath(msg, name) | ||||
| 				} | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/hex" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"log" | ||||
| @ -15,6 +16,9 @@ import ( | ||||
| 	"hakurei.app/message" | ||||
| ) | ||||
| 
 | ||||
| // tryPath attempts to read [hst.Config] from multiple sources. | ||||
| // tryPath reads from [os.Stdin] if name has value "-". | ||||
| // Otherwise, name is passed to tryFd, and if that returns nil, name is passed to [os.Open]. | ||||
| func tryPath(msg message.Msg, name string) (config *hst.Config) { | ||||
| 	var r io.ReadCloser | ||||
| 	config = new(hst.Config) | ||||
| @ -42,6 +46,7 @@ func tryPath(msg message.Msg, name string) (config *hst.Config) { | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // tryFd returns a [io.ReadCloser] if name represents an integer corresponding to a valid file descriptor. | ||||
| func tryFd(msg message.Msg, name string) io.ReadCloser { | ||||
| 	if v, err := strconv.Atoi(name); err != nil { | ||||
| 		if !errors.Is(err, strconv.ErrSyntax) { | ||||
| @ -61,10 +66,48 @@ func tryFd(msg message.Msg, name string) io.ReadCloser { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func tryShort(msg message.Msg, name string) (config *hst.Config, entry *hst.State) { | ||||
| 	likePrefix := false | ||||
| 	if len(name) <= 32 { | ||||
| 		likePrefix = true | ||||
| // shortLengthMin is the minimum length a short form identifier can have and still be interpreted as an identifier. | ||||
| const shortLengthMin = 1 << 3 | ||||
| 
 | ||||
| // shortIdentifier returns an eight character short representation of [hst.ID] from its random bytes. | ||||
| func shortIdentifier(id *hst.ID) string { | ||||
| 	return shortIdentifierString(id.String()) | ||||
| } | ||||
| 
 | ||||
| // shortIdentifierString implements shortIdentifier on an arbitrary string. | ||||
| func shortIdentifierString(s string) string { | ||||
| 	return s[len(hst.ID{}) : len(hst.ID{})+shortLengthMin] | ||||
| } | ||||
| 
 | ||||
| // tryIdentifier attempts to match [hst.State] from a [hex] representation of [hst.ID] or a prefix of its lower half. | ||||
| func tryIdentifier(msg message.Msg, name string) (config *hst.Config, entry *hst.State) { | ||||
| 	return tryIdentifierEntries(msg, name, func() map[hst.ID]*hst.State { | ||||
| 		var sc hst.Paths | ||||
| 		app.CopyPaths().Copy(&sc, new(app.Hsu).MustID(nil)) | ||||
| 		s := state.NewMulti(msg, sc.RunDirPath.String()) | ||||
| 		if entries, err := state.Join(s); err != nil { | ||||
| 			msg.GetLogger().Printf("cannot join store: %v", err) // not fatal | ||||
| 			return nil | ||||
| 		} else { | ||||
| 			return entries | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // tryIdentifierEntries implements tryIdentifier with a custom entries pair getter. | ||||
| func tryIdentifierEntries( | ||||
| 	msg message.Msg, | ||||
| 	name string, | ||||
| 	getEntries func() map[hst.ID]*hst.State, | ||||
| ) (config *hst.Config, entry *hst.State) { | ||||
| 	const ( | ||||
| 		likeShort = 1 << iota | ||||
| 		likeFull | ||||
| 	) | ||||
| 
 | ||||
| 	var likely uintptr | ||||
| 	if len(name) >= shortLengthMin && len(name) <= len(hst.ID{}) { // half the hex representation | ||||
| 		// cannot safely decode here due to unknown alignment | ||||
| 		for _, c := range name { | ||||
| 			if c >= '0' && c <= '9' { | ||||
| 				continue | ||||
| @ -72,25 +115,27 @@ func tryShort(msg message.Msg, name string) (config *hst.Config, entry *hst.Stat | ||||
| 			if c >= 'a' && c <= 'f' { | ||||
| 				continue | ||||
| 			} | ||||
| 			likePrefix = false | ||||
| 			break | ||||
| 			return | ||||
| 		} | ||||
| 		likely |= likeShort | ||||
| 	} else if len(name) == hex.EncodedLen(len(hst.ID{})) { | ||||
| 		likely |= likeFull | ||||
| 	} | ||||
| 
 | ||||
| 	// try to match from state store | ||||
| 	if likePrefix && len(name) >= 8 { | ||||
| 		msg.Verbose("argument looks like prefix") | ||||
| 	if likely == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 	entries := getEntries() | ||||
| 	if entries == nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 		var sc hst.Paths | ||||
| 		app.CopyPaths().Copy(&sc, new(app.Hsu).MustID(nil)) | ||||
| 		s := state.NewMulti(msg, sc.RunDirPath.String()) | ||||
| 		if entries, err := state.Join(s); err != nil { | ||||
| 			log.Printf("cannot join store: %v", err) | ||||
| 			// drop to fetch from file | ||||
| 		} else { | ||||
| 	switch { | ||||
| 	case likely&likeShort != 0: | ||||
| 		msg.Verbose("argument looks like short identifier") | ||||
| 		for id := range entries { | ||||
| 			v := id.String() | ||||
| 				if strings.HasPrefix(v, name) { | ||||
| 			if strings.HasPrefix(v[len(hst.ID{}):], name) { | ||||
| 				// match, use config from this state entry | ||||
| 				entry = entries[id] | ||||
| 				config = entry.Config | ||||
| @ -99,8 +144,21 @@ func tryShort(msg message.Msg, name string) (config *hst.Config, entry *hst.Stat | ||||
| 
 | ||||
| 			msg.Verbosef("instance %s skipped", v) | ||||
| 		} | ||||
| 		} | ||||
| 	} | ||||
| 		return | ||||
| 
 | ||||
| 	case likely&likeFull != 0: | ||||
| 		var likelyID hst.ID | ||||
| 		if likelyID.UnmarshalText([]byte(name)) != nil { | ||||
| 			return | ||||
| 		} | ||||
| 		msg.Verbose("argument looks like identifier") | ||||
| 		if ent, ok := entries[likelyID]; ok { | ||||
| 			entry = ent | ||||
| 			config = ent.Config | ||||
| 		} | ||||
| 		return | ||||
| 
 | ||||
| 	default: | ||||
| 		panic("unreachable") | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										101
									
								
								cmd/hakurei/parse_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								cmd/hakurei/parse_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,101 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"hakurei.app/hst" | ||||
| 	"hakurei.app/message" | ||||
| ) | ||||
| 
 | ||||
| func TestShortIdentifier(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 	id := hst.ID{ | ||||
| 		0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, | ||||
| 		0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, | ||||
| 	} | ||||
| 
 | ||||
| 	const want = "fedcba98" | ||||
| 	if got := shortIdentifier(&id); got != want { | ||||
| 		t.Errorf("shortIdentifier: %q, want %q", got, want) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestTryIdentifier(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 	msg := message.NewMsg(nil) | ||||
| 	id := hst.ID{ | ||||
| 		0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, | ||||
| 		0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, | ||||
| 	} | ||||
| 
 | ||||
| 	testCases := []struct { | ||||
| 		name    string | ||||
| 		s       string | ||||
| 		entries map[hst.ID]*hst.State | ||||
| 		want    *hst.State | ||||
| 	}{ | ||||
| 		{"likely entries fault", "ffffffff", nil, nil}, | ||||
| 
 | ||||
| 		{"likely short too short", "ff", nil, nil}, | ||||
| 		{"likely short too long", "fffffffffffffffff", nil, nil}, | ||||
| 		{"likely short invalid lower", "fffffff\x00", nil, nil}, | ||||
| 		{"likely short invalid higher", "0000000\xff", nil, nil}, | ||||
| 		{"short no match", "fedcba98", map[hst.ID]*hst.State{hst.ID{}: nil}, nil}, | ||||
| 		{"short match", "fedcba98", map[hst.ID]*hst.State{ | ||||
| 			hst.ID{}: nil, | ||||
| 			id: { | ||||
| 				ID:      id, | ||||
| 				PID:     0xcafebabe, | ||||
| 				ShimPID: 0xdeadbeef, | ||||
| 				Config:  hst.Template(), | ||||
| 			}, | ||||
| 		}, &hst.State{ | ||||
| 			ID:      id, | ||||
| 			PID:     0xcafebabe, | ||||
| 			ShimPID: 0xdeadbeef, | ||||
| 			Config:  hst.Template(), | ||||
| 		}}, | ||||
| 		{"short match longer", "fedcba98765", map[hst.ID]*hst.State{ | ||||
| 			hst.ID{}: nil, | ||||
| 			id: { | ||||
| 				ID:      id, | ||||
| 				PID:     0xcafebabe, | ||||
| 				ShimPID: 0xdeadbeef, | ||||
| 				Config:  hst.Template(), | ||||
| 			}, | ||||
| 		}, &hst.State{ | ||||
| 			ID:      id, | ||||
| 			PID:     0xcafebabe, | ||||
| 			ShimPID: 0xdeadbeef, | ||||
| 			Config:  hst.Template(), | ||||
| 		}}, | ||||
| 
 | ||||
| 		{"likely long invalid", "0123456789abcdeffedcba987654321\x00", map[hst.ID]*hst.State{}, nil}, | ||||
| 		{"long no match", "0123456789abcdeffedcba9876543210", map[hst.ID]*hst.State{hst.ID{}: nil}, nil}, | ||||
| 		{"long match", "0123456789abcdeffedcba9876543210", map[hst.ID]*hst.State{ | ||||
| 			hst.ID{}: nil, | ||||
| 			id: { | ||||
| 				ID:      id, | ||||
| 				PID:     0xcafebabe, | ||||
| 				ShimPID: 0xdeadbeef, | ||||
| 				Config:  hst.Template(), | ||||
| 			}, | ||||
| 		}, &hst.State{ | ||||
| 			ID:      id, | ||||
| 			PID:     0xcafebabe, | ||||
| 			ShimPID: 0xdeadbeef, | ||||
| 			Config:  hst.Template(), | ||||
| 		}}, | ||||
| 	} | ||||
| 	for _, tc := range testCases { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| 			t.Parallel() | ||||
| 
 | ||||
| 			_, got := tryIdentifierEntries(msg, tc.s, func() map[hst.ID]*hst.State { return tc.entries }) | ||||
| 			if !reflect.DeepEqual(got, tc.want) { | ||||
| 				t.Errorf("tryIdentifier: %#v, want %#v", got, tc.want) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| @ -215,7 +215,7 @@ func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON boo | ||||
| 			encodeJSON(log.Fatal, output, short, v) | ||||
| 		} else { | ||||
| 			for _, e := range exp { | ||||
| 				mustPrintln(output, e.s[:8]) | ||||
| 				mustPrintln(output, shortIdentifierString(e.s)) | ||||
| 			} | ||||
| 		} | ||||
| 		return | ||||
| @ -237,12 +237,12 @@ func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON boo | ||||
| 			as = strconv.Itoa(e.Config.Identity) | ||||
| 			id := e.Config.ID | ||||
| 			if id == "" { | ||||
| 				id = "app.hakurei." + e.s[:8] | ||||
| 				id = "app.hakurei." + shortIdentifierString(e.s) | ||||
| 			} | ||||
| 			as += " (" + id + ")" | ||||
| 		} | ||||
| 		t.Printf("\t%s\t%d\t%s\t%s\n", | ||||
| 			e.s[:8], e.PID, as, now.Sub(e.Time).Round(time.Second).String()) | ||||
| 			shortIdentifierString(e.s), e.PID, as, now.Sub(e.Time).Round(time.Second).String()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -523,13 +523,13 @@ func TestPrintPs(t *testing.T) { | ||||
| 		{"state corruption", map[hst.ID]*hst.State{hst.ID{}: testState}, false, false, "    Instance    PID    Application    Uptime\n"}, | ||||
| 
 | ||||
| 		{"valid pd", map[hst.ID]*hst.State{testID: {ID: testID, PID: 1 << 8, Config: new(hst.Config), Time: testAppTime}}, false, false, `    Instance    PID    Application                 Uptime | ||||
|     8e2c76b0    256    0 (app.hakurei.8e2c76b0)    1h2m32s | ||||
|     4cf073bd    256    0 (app.hakurei.4cf073bd)    1h2m32s | ||||
| `}, | ||||
| 
 | ||||
| 		{"valid", map[hst.ID]*hst.State{testID: testState}, false, false, `    Instance    PID           Application                  Uptime | ||||
|     8e2c76b0    3405691582    9 (org.chromium.Chromium)    1h2m32s | ||||
|     4cf073bd    3405691582    9 (org.chromium.Chromium)    1h2m32s | ||||
| `}, | ||||
| 		{"valid short", map[hst.ID]*hst.State{testID: testState}, true, false, "8e2c76b0\n"}, | ||||
| 		{"valid short", map[hst.ID]*hst.State{testID: testState}, true, false, "4cf073bd\n"}, | ||||
| 		{"valid json", map[hst.ID]*hst.State{testID: testState}, false, true, `{ | ||||
|   "8e2c76b066dabe574cf073bdb46eb5c1": { | ||||
|     "instance": "8e2c76b066dabe574cf073bdb46eb5c1", | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user