gitee.com/mysnapcore/mysnapd@v0.1.0/cmd/snap/inhibit.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main 21 22 import ( 23 "context" 24 "fmt" 25 "os" 26 "time" 27 28 "gitee.com/mysnapcore/mysnapd/cmd/snaplock/runinhibit" 29 "gitee.com/mysnapcore/mysnapd/i18n" 30 "gitee.com/mysnapcore/mysnapd/progress" 31 "gitee.com/mysnapcore/mysnapd/usersession/client" 32 ) 33 34 func waitWhileInhibited(snapName string) error { 35 hint, err := runinhibit.IsLocked(snapName) 36 if err != nil { 37 return err 38 } 39 if hint == runinhibit.HintNotInhibited { 40 return nil 41 } 42 43 // wait for HintInhibitedForRefresh set by gate-auto-refresh hook handler 44 // when it has finished; the hook starts with HintInhibitedGateRefresh lock 45 // and then either unlocks it or changes to HintInhibitedForRefresh (see 46 // gateAutoRefreshHookHandler in hooks.go). 47 // waitInhibitUnlock will return also on HintNotInhibited. 48 notInhibited, err := waitInhibitUnlock(snapName, runinhibit.HintInhibitedForRefresh) 49 if err != nil { 50 return err 51 } 52 if notInhibited { 53 return nil 54 } 55 56 if isGraphicalSession() { 57 return graphicalSessionFlow(snapName, hint) 58 } 59 // terminal and headless 60 return textFlow(snapName, hint) 61 } 62 63 func inhibitMessage(snapName string, hint runinhibit.Hint) string { 64 switch hint { 65 case runinhibit.HintInhibitedForRefresh: 66 return fmt.Sprintf(i18n.G("snap package %q is being refreshed, please wait"), snapName) 67 default: 68 return fmt.Sprintf(i18n.G("snap package cannot be used now: %s"), string(hint)) 69 } 70 } 71 72 var isGraphicalSession = func() bool { 73 return os.Getenv("DISPLAY") != "" || os.Getenv("WAYLAND_DISPLAY") != "" 74 } 75 76 var pendingRefreshNotification = func(refreshInfo *client.PendingSnapRefreshInfo) error { 77 userclient := client.NewForUids(os.Getuid()) 78 if err := userclient.PendingRefreshNotification(context.TODO(), refreshInfo); err != nil { 79 return err 80 } 81 return nil 82 } 83 84 var finishRefreshNotification = func(refreshInfo *client.FinishedSnapRefreshInfo) error { 85 userclient := client.NewForUids(os.Getuid()) 86 if err := userclient.FinishRefreshNotification(context.TODO(), refreshInfo); err != nil { 87 return err 88 } 89 return nil 90 } 91 92 func graphicalSessionFlow(snapName string, hint runinhibit.Hint) error { 93 refreshInfo := client.PendingSnapRefreshInfo{ 94 InstanceName: snapName, 95 // Remaining time = 0 results in "Snap .. is refreshing now" message from 96 // usersession agent. 97 TimeRemaining: 0, 98 } 99 100 if err := pendingRefreshNotification(&refreshInfo); err != nil { 101 return err 102 } 103 if _, err := waitInhibitUnlock(snapName, runinhibit.HintNotInhibited); err != nil { 104 return err 105 } 106 107 finishRefreshInfo := client.FinishedSnapRefreshInfo{InstanceName: snapName} 108 return finishRefreshNotification(&finishRefreshInfo) 109 } 110 111 func textFlow(snapName string, hint runinhibit.Hint) error { 112 fmt.Fprintf(Stdout, "%s\n", inhibitMessage(snapName, hint)) 113 pb := progress.MakeProgressBar(Stdout) 114 pb.Spin(i18n.G("please wait...")) 115 _, err := waitInhibitUnlock(snapName, runinhibit.HintNotInhibited) 116 pb.Finished() 117 return err 118 } 119 120 var isLocked = runinhibit.IsLocked 121 122 // waitInhibitUnlock waits until the runinhibit lock hint has a specific waitFor value 123 // or isn't inhibited anymore. 124 var waitInhibitUnlock = func(snapName string, waitFor runinhibit.Hint) (notInhibited bool, err error) { 125 // Every 0.5s check if the inhibition file is still present. 126 ticker := time.NewTicker(500 * time.Millisecond) 127 defer ticker.Stop() 128 for { 129 select { 130 case <-ticker.C: 131 // Half a second has elapsed, let's check again. 132 hint, err := isLocked(snapName) 133 if err != nil { 134 return false, err 135 } 136 if hint == runinhibit.HintNotInhibited { 137 return true, nil 138 } 139 if hint == waitFor { 140 return false, nil 141 } 142 } 143 } 144 }