github.com/ethanhsieh/snapd@v0.0.0-20210615102523-3db9b8e4edc5/cmd/snap/cmd_userd.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 // +build !darwin 3 4 /* 5 * Copyright (C) 2017-2019 Canonical Ltd 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 3 as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 package main 22 23 import ( 24 "fmt" 25 "os" 26 "os/signal" 27 "path/filepath" 28 "syscall" 29 30 "github.com/jessevdk/go-flags" 31 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/i18n" 34 "github.com/snapcore/snapd/snapdtool" 35 "github.com/snapcore/snapd/usersession/agent" 36 "github.com/snapcore/snapd/usersession/autostart" 37 "github.com/snapcore/snapd/usersession/userd" 38 ) 39 40 type cmdUserd struct { 41 Autostart bool `long:"autostart"` 42 Agent bool `long:"agent"` 43 } 44 45 var shortUserdHelp = i18n.G("Start the userd service") 46 var longUserdHelp = i18n.G(` 47 The userd command starts the snap user session service. 48 `) 49 50 func init() { 51 cmd := addCommand("userd", 52 shortUserdHelp, 53 longUserdHelp, 54 func() flags.Commander { 55 return &cmdUserd{} 56 }, map[string]string{ 57 // TRANSLATORS: This should not start with a lowercase letter. 58 "autostart": i18n.G("Autostart user applications"), 59 // TRANSLATORS: This should not start with a lowercase letter. 60 "agent": i18n.G("Run the user session agent"), 61 }, nil) 62 cmd.hidden = true 63 } 64 65 var osChmod = os.Chmod 66 67 func maybeFixupUsrSnapPermissions() error { 68 usr, err := userCurrent() 69 if err != nil { 70 return err 71 } 72 73 usrSnapDir := filepath.Join(usr.HomeDir, dirs.UserHomeSnapDir) 74 75 // restrict the user's "snap dir", i.e. /home/$USER/snap, to be private with 76 // permissions o0700 so that other users cannot read the data there, some 77 // snaps such as chromium etc may store secrets inside this directory 78 // note that this operation is safe since `userd --autostart` runs as the 79 // user so there is no issue with this modification being performed as root, 80 // and being vulnerable to symlink switching attacks, etc. 81 if err := osChmod(usrSnapDir, 0700); err != nil { 82 // if the dir doesn't exist for some reason (i.e. maybe this user has 83 // never used snaps but snapd is still installed) then ignore the error 84 if !os.IsNotExist(err) { 85 return fmt.Errorf("cannot restrict user snap home dir %q: %v", usrSnapDir, err) 86 } 87 } 88 89 return nil 90 } 91 92 func (x *cmdUserd) Execute(args []string) error { 93 if len(args) > 0 { 94 return ErrExtraArgs 95 } 96 97 if x.Autostart { 98 // autostart is called when starting the graphical session, use that as 99 // an opportunity to fix ~/snap permission bits 100 if err := maybeFixupUsrSnapPermissions(); err != nil { 101 fmt.Fprintf(Stderr, "failure fixing ~/snap permissions: %v\n", err) 102 } 103 104 return x.runAutostart() 105 } 106 107 if x.Agent { 108 return x.runAgent() 109 } 110 111 return x.runUserd() 112 } 113 114 var signalNotify = signalNotifyImpl 115 116 func (x *cmdUserd) runUserd() error { 117 var userd userd.Userd 118 if err := userd.Init(); err != nil { 119 return err 120 } 121 userd.Start() 122 123 ch, stop := signalNotify(syscall.SIGINT, syscall.SIGTERM) 124 defer stop() 125 126 select { 127 case sig := <-ch: 128 fmt.Fprintf(Stdout, "Exiting on %s.\n", sig) 129 case <-userd.Dying(): 130 // something called Stop() 131 } 132 133 return userd.Stop() 134 } 135 136 func (x *cmdUserd) runAgent() error { 137 agent, err := agent.New() 138 if err != nil { 139 return err 140 } 141 agent.Version = snapdtool.Version 142 agent.Start() 143 144 ch, stop := signalNotify(syscall.SIGINT, syscall.SIGTERM) 145 defer stop() 146 147 select { 148 case sig := <-ch: 149 fmt.Fprintf(Stdout, "Exiting on %s.\n", sig) 150 case <-agent.Dying(): 151 // something called Stop() 152 } 153 154 return agent.Stop() 155 } 156 157 func (x *cmdUserd) runAutostart() error { 158 if err := autostart.AutostartSessionApps(); err != nil { 159 return fmt.Errorf("autostart failed for the following apps:\n%v", err) 160 } 161 return nil 162 } 163 164 func signalNotifyImpl(sig ...os.Signal) (ch chan os.Signal, stop func()) { 165 ch = make(chan os.Signal, len(sig)) 166 signal.Notify(ch, sig...) 167 stop = func() { signal.Stop(ch) } 168 return ch, stop 169 }