@ -48,19 +48,26 @@ func (u *userMeta) GroupIds() ([]string, error) {
return u . User . GroupIds ( )
return u . User . GroupIds ( )
}
}
// userLookup is like os/user.Lookup Id but it returns a *userMeta wrapper
// userLookup is like os/user.Lookup but it returns a *userMeta wrapper
// around a *user.User with extra fields.
// around a *user.User with extra fields.
func userLookup ( u id string ) ( * userMeta , error ) {
func userLookup ( u sername string ) ( * userMeta , error ) {
if runtime . GOOS != "linux" {
if runtime . GOOS != "linux" {
return userLookupStd ( u id )
return userLookupStd ( u sername )
}
}
// No getent on Gokrazy. So hard-code the login shell.
// No getent on Gokrazy. So hard-code the login shell.
if distro . Get ( ) == distro . Gokrazy {
if distro . Get ( ) == distro . Gokrazy {
um , err := userLookupStd ( uid )
um , err := userLookupStd ( username )
if err == nil {
if err != nil {
um . loginShellCached = "/tmp/serial-busybox/ash"
um . User = user . User {
Uid : "0" ,
Gid : "0" ,
Username : "root" ,
Name : "Gokrazy" ,
HomeDir : "/" ,
}
}
}
um . loginShellCached = "/tmp/serial-busybox/ash"
return um , err
return um , err
}
}
@ -70,7 +77,7 @@ func userLookup(uid string) (*userMeta, error) {
// os/user without cgo won't get (because of no libc hooks).
// os/user without cgo won't get (because of no libc hooks).
// But if "getent" fails, userLookupGetent falls back to the standard
// But if "getent" fails, userLookupGetent falls back to the standard
// library anyway.
// library anyway.
return userLookupGetent ( u id )
return userLookupGetent ( u sername )
}
}
func validUsername ( uid string ) bool {
func validUsername ( uid string ) bool {
@ -85,18 +92,18 @@ func validUsername(uid string) bool {
return true
return true
}
}
func userLookupGetent ( u id string ) ( * userMeta , error ) {
func userLookupGetent ( u sername string ) ( * userMeta , error ) {
// Do some basic validation before passing this string to "getent", even though
// Do some basic validation before passing this string to "getent", even though
// getent should do its own validation.
// getent should do its own validation.
if ! validUsername ( u id ) {
if ! validUsername ( u sername ) {
return nil , errors . New ( "invalid username" )
return nil , errors . New ( "invalid username" )
}
}
ctx , cancel := context . WithTimeout ( context . Background ( ) , 10 * time . Second )
ctx , cancel := context . WithTimeout ( context . Background ( ) , 10 * time . Second )
defer cancel ( )
defer cancel ( )
out , err := exec . CommandContext ( ctx , "getent" , "passwd" , u id ) . Output ( )
out , err := exec . CommandContext ( ctx , "getent" , "passwd" , u sername ) . Output ( )
if err != nil {
if err != nil {
log . Printf ( "error calling getent for user %q: %v" , u id , err )
log . Printf ( "error calling getent for user %q: %v" , u sername , err )
return userLookupStd ( u id )
return userLookupStd ( u sername )
}
}
// output is "alice:x:1001:1001:Alice Smith,,,:/home/alice:/bin/bash"
// output is "alice:x:1001:1001:Alice Smith,,,:/home/alice:/bin/bash"
f := strings . SplitN ( strings . TrimSpace ( string ( out ) ) , ":" , 10 )
f := strings . SplitN ( strings . TrimSpace ( string ( out ) ) , ":" , 10 )
@ -116,8 +123,8 @@ func userLookupGetent(uid string) (*userMeta, error) {
return um , nil
return um , nil
}
}
func userLookupStd ( u id string ) ( * userMeta , error ) {
func userLookupStd ( u sername string ) ( * userMeta , error ) {
u , err := user . Lookup Id( uid )
u , err := user . Lookup ( username )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}