diff --git a/main.go b/main.go index 5d08b81e..aed019c7 100644 --- a/main.go +++ b/main.go @@ -31,7 +31,7 @@ func init() { func main() { // Set Info - info.Set("Portmaster", "0.2.5", "AGPLv3", true) + info.Set("Portmaster", "0.3.0", "AGPLv3", true) // Start err := modules.Start() diff --git a/pack_core b/pack_core index 363fc567..edd368f4 100755 --- a/pack_core +++ b/pack_core @@ -16,7 +16,7 @@ function check { # get version version=$(grep "info.Set" main.go | cut -d'"' -f4) # build versioned file name - filename="portmaster_v${version//./-}" + filename="portmaster-core_v${version//./-}" # platform platform="${GOOS}_${GOARCH}" if [[ $GOOS == "windows" ]]; then @@ -40,7 +40,7 @@ function build { # get version version=$(grep "info.Set" main.go | cut -d'"' -f4) # build versioned file name - filename="portmaster_v${version//./-}" + filename="portmaster-core_v${version//./-}" # platform platform="${GOOS}_${GOARCH}" if [[ $GOOS == "windows" ]]; then diff --git a/pmctl/get.go b/pmctl/get.go index 3c01b711..6457ddba 100644 --- a/pmctl/get.go +++ b/pmctl/get.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/safing/portbase/utils" "github.com/safing/portmaster/updates" ) @@ -25,7 +26,7 @@ func getFile(identifier string) (*updates.File, error) { if err != nil { if os.IsNotExist(err) { // create dirs - err = updates.CheckDir(updateStoragePath) + err = utils.EnsureDirectory(updateStoragePath, 0755) if err != nil { return nil, err } diff --git a/pmctl/main.go b/pmctl/main.go index b9dee2ca..320d2ccf 100644 --- a/pmctl/main.go +++ b/pmctl/main.go @@ -14,7 +14,7 @@ import ( ) const ( - logPrefix = "[pmctl]" + logPrefix = "[control]" ) var ( @@ -22,7 +22,7 @@ var ( databaseRootDir *string rootCmd = &cobra.Command{ - Use: "pmctl", + Use: "portmaster-control", Short: "contoller for all portmaster components", PersistentPreRunE: initPmCtl, RunE: func(cmd *cobra.Command, args []string) error { @@ -55,7 +55,7 @@ func main() { // }() // set meta info - info.Set("Portmaster Control", "0.1.3", "AGPLv3", true) + info.Set("Portmaster Control", "0.2.0", "AGPLv3", true) // check if meta info is ok err := info.CheckVersion() @@ -97,7 +97,7 @@ func initPmCtl(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("%s failed to upgrade self: %s", logPrefix, err) } - fmt.Println("upgraded pmctl") + fmt.Println("upgraded portmaster-control") } return nil diff --git a/pmctl/pack b/pmctl/pack index 3aa44b82..6e050ec4 100755 --- a/pmctl/pack +++ b/pmctl/pack @@ -8,7 +8,7 @@ COL_BOLD="\033[01;01m" COL_RED="\033[31m" destDirPart1="../dist" -destDirPart2="pmctl" +destDirPart2="control" function check { # output @@ -16,7 +16,7 @@ function check { # get version version=$(grep "info.Set" main.go | cut -d'"' -f4) # build versioned file name - filename="pmctl_v${version//./-}" + filename="portmaster-control_v${version//./-}" # platform platform="${GOOS}_${GOARCH}" if [[ $GOOS == "windows" ]]; then @@ -28,9 +28,9 @@ function check { # check if file exists if [[ -f $destPath ]]; then - echo "[pmctl] $platform $version already built" + echo "[control] $platform $version already built" else - echo -e "${COL_BOLD}[pmctl] $platform $version${COL_OFF}" + echo -e "${COL_BOLD}[control] $platform $version${COL_OFF}" fi } @@ -40,7 +40,7 @@ function build { # get version version=$(grep "info.Set" main.go | cut -d'"' -f4) # build versioned file name - filename="pmctl_v${version//./-}" + filename="portmaster-control_v${version//./-}" # platform platform="${GOOS}_${GOARCH}" if [[ $GOOS == "windows" ]]; then @@ -52,19 +52,19 @@ function build { # check if file exists if [[ -f $destPath ]]; then - echo "[pmctl] $platform already built in version $version, skipping..." + echo "[control] $platform already built in version $version, skipping..." return fi # build ./build if [[ $? -ne 0 ]]; then - echo -e "\n${COL_BOLD}[pmctl] $platform: ${COL_RED}BUILD FAILED.${COL_OFF}" + echo -e "\n${COL_BOLD}[control] $platform: ${COL_RED}BUILD FAILED.${COL_OFF}" exit 1 fi mkdir -p $(dirname $destPath) cp $output $destPath - echo -e "\n${COL_BOLD}[pmctl] $platform: successfully built.${COL_OFF}" + echo -e "\n${COL_BOLD}[control] $platform: successfully built.${COL_OFF}" } function check_all { diff --git a/pmctl/run.go b/pmctl/run.go index 401f42ad..978f47b1 100644 --- a/pmctl/run.go +++ b/pmctl/run.go @@ -27,7 +27,7 @@ var runCore = &cobra.Command{ Use: "core", Short: "Run the Portmaster Core", RunE: func(cmd *cobra.Command, args []string) error { - return run("core/portmaster", cmd, false) + return run("core/portmaster-core", cmd, false) }, FParseErrWhitelist: cobra.FParseErrWhitelist{ // UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags diff --git a/pmctl/upgrade.go b/pmctl/upgrade.go index 411070bf..c404ec05 100644 --- a/pmctl/upgrade.go +++ b/pmctl/upgrade.go @@ -13,7 +13,7 @@ import ( func checkForUpgrade() (update *updates.File) { info := info.GetInfo() - file, err := updates.GetLocalPlatformFile("pmctl/pmctl") + file, err := updates.GetLocalPlatformFile("control/portmaster-control") if err != nil { return nil } @@ -113,6 +113,6 @@ func removeOldBin() error { return nil } - fmt.Println("removed previous pmctl") + fmt.Println("removed previous portmaster-control") return nil } diff --git a/ui/serve.go b/ui/serve.go index dbf5637f..659d4d1c 100644 --- a/ui/serve.go +++ b/ui/serve.go @@ -11,7 +11,6 @@ import ( "sync" resources "github.com/cookieo9/resources-go" - "github.com/gorilla/mux" "github.com/safing/portbase/api" "github.com/safing/portbase/log" @@ -41,7 +40,7 @@ func ServeBundle(defaultModuleName string) func(w http.ResponseWriter, r *http.R // log.Tracef("ui: request for %s", r.RequestURI) - vars := mux.Vars(r) + vars := api.GetMuxVars(r) moduleName, ok := vars["moduleName"] if !ok { moduleName = defaultModuleName @@ -98,8 +97,13 @@ func ServeBundle(defaultModuleName string) func(w http.ResponseWriter, r *http.R func ServeFileFromBundle(w http.ResponseWriter, r *http.Request, bundleName string, bundle *resources.BundleSequence, path string) { readCloser, err := bundle.Open(path) if err != nil { - log.Tracef("ui: error opening module %s: %s", bundleName, err) - http.Error(w, err.Error(), http.StatusInternalServerError) + if err == resources.ErrNotFound { + log.Tracef("ui: requested resource \"%s\" not found in bundle %s: %s", path, bundleName, err) + http.Error(w, err.Error(), http.StatusNotFound) + } else { + log.Tracef("ui: error opening module %s: %s", bundleName, err) + http.Error(w, err.Error(), http.StatusInternalServerError) + } return } diff --git a/updates/fetch.go b/updates/fetch.go index f1cd07de..4d3d3c02 100644 --- a/updates/fetch.go +++ b/updates/fetch.go @@ -15,6 +15,7 @@ import ( "github.com/google/renameio" "github.com/safing/portbase/log" + "github.com/safing/portbase/utils" ) var ( @@ -37,15 +38,15 @@ func fetchFile(realFilepath, updateFilepath string, tries int) error { // check destination dir dirPath := filepath.Dir(realFilepath) - err = CheckDir(dirPath) + err = utils.EnsureDirectory(dirPath, 0755) if err != nil { - return fmt.Errorf("updates: could not create updates folder: %s", dirPath) + return fmt.Errorf("could not create updates folder: %s", dirPath) } // open file for writing - atomicFile, err := renameio.TempFile(filepath.Join(updateStoragePath, "tmp"), realFilepath) + atomicFile, err := renameio.TempFile(downloadTmpPath, realFilepath) if err != nil { - return fmt.Errorf("updates: could not create temp file for download: %s", err) + return fmt.Errorf("could not create temp file for download: %s", err) } defer atomicFile.Cleanup() @@ -62,7 +63,7 @@ func fetchFile(realFilepath, updateFilepath string, tries int) error { return fmt.Errorf("failed downloading %s: %s", downloadURL, err) } if resp.ContentLength != n { - return fmt.Errorf("download unfinished, written %d out of %d bytes.", n, resp.ContentLength) + return fmt.Errorf("download unfinished, written %d out of %d bytes", n, resp.ContentLength) } // finalize file @@ -72,7 +73,8 @@ func fetchFile(realFilepath, updateFilepath string, tries int) error { } // set permissions if runtime.GOOS != "windows" { - err = os.Chmod(realFilepath, 0644) + // FIXME: only set executable files to 0755, set other to 0644 + err = os.Chmod(realFilepath, 0755) if err != nil { log.Warningf("updates: failed to set permissions on downloaded file %s: %s", realFilepath, err) } @@ -108,7 +110,7 @@ func fetchData(downloadPath string, tries int) ([]byte, error) { return nil, fmt.Errorf("failed downloading %s: %s", downloadURL, err) } if resp.ContentLength != n { - return nil, fmt.Errorf("download unfinished, written %d out of %d bytes.", n, resp.ContentLength) + return nil, fmt.Errorf("download unfinished, written %d out of %d bytes", n, resp.ContentLength) } return buf.Bytes(), nil diff --git a/updates/file.go b/updates/file.go index 4850dc58..5fbab847 100644 --- a/updates/file.go +++ b/updates/file.go @@ -7,6 +7,7 @@ type File struct { stable bool } +// NewFile combines update file attributes into an easy to use object. func NewFile(filepath string, version string, stable bool) *File { return &File{ filepath: filepath, diff --git a/updates/filename.go b/updates/filename.go index ae565c24..1768255d 100644 --- a/updates/filename.go +++ b/updates/filename.go @@ -8,6 +8,7 @@ import ( var versionRegex = regexp.MustCompile("_v[0-9]+-[0-9]+-[0-9]+b?") +// GetIdentifierAndVersion splits the given file path into its identifier and version. func GetIdentifierAndVersion(versionedPath string) (identifier, version string, ok bool) { // extract version rawVersion := versionRegex.FindString(versionedPath) @@ -27,6 +28,7 @@ func GetIdentifierAndVersion(versionedPath string) (identifier, version string, return versionedPath[:i] + versionedPath[i+len(rawVersion):], version, true } +// GetVersionedPath combines the identifier and version and returns it as a file path. func GetVersionedPath(identifier, version string) (versionedPath string) { // split in half splittedFilePath := strings.SplitN(identifier, ".", 2) diff --git a/updates/get.go b/updates/get.go index 3ac13b8e..9d4b7ac8 100644 --- a/updates/get.go +++ b/updates/get.go @@ -9,10 +9,12 @@ import ( "runtime" "github.com/safing/portbase/log" + "github.com/safing/portbase/utils" ) +// Errors var ( - ErrNotFound = errors.New("the requested file could not be found") + ErrNotFound = errors.New("the requested file could not be found") ErrNotAvailableLocally = errors.New("the requested file is not available locally") ) @@ -81,12 +83,12 @@ func loadOrFetchFile(identifier string, fetch bool) (*File, error) { } // check download dir - err := CheckDir(filepath.Join(updateStoragePath, "tmp")) + err := utils.EnsureDirectory(downloadTmpPath, 0755) if err != nil { return nil, fmt.Errorf("could not prepare tmp directory for download: %s", err) } - if (!fetch) { + if !fetch { return nil, ErrNotAvailableLocally } diff --git a/updates/latest.go b/updates/latest.go index 83bb11b6..57109a7f 100644 --- a/updates/latest.go +++ b/updates/latest.go @@ -62,6 +62,7 @@ func LoadLatest() error { return nil } +// ScanForLatest scan the local update directory and returns a map of the latest/newest component versions. func ScanForLatest(baseDir string, hardFail bool) (latest map[string]string, lastError error) { var added int latest = make(map[string]string) @@ -117,6 +118,7 @@ func ScanForLatest(baseDir string, hardFail bool) (latest map[string]string, las return latest, nil } +// LoadIndexes loads the current update indexes from disk. func LoadIndexes() error { data, err := ioutil.ReadFile(filepath.Join(updateStoragePath, "stable.json")) if err != nil { diff --git a/updates/main.go b/updates/main.go index 3b9e68cc..395e2f92 100644 --- a/updates/main.go +++ b/updates/main.go @@ -2,7 +2,6 @@ package updates import ( "errors" - "fmt" "os" "path/filepath" @@ -10,21 +9,24 @@ import ( "github.com/safing/portbase/info" "github.com/safing/portbase/log" "github.com/safing/portbase/modules" + "github.com/safing/portbase/utils" ) var ( updateStoragePath string + downloadTmpPath string ) // SetDatabaseRoot tells the updates module where the database is - and where to put its stuff. func SetDatabaseRoot(path string) { if updateStoragePath == "" { updateStoragePath = filepath.Join(path, "updates") + downloadTmpPath = filepath.Join(updateStoragePath, "tmp") } } func init() { - modules.Register("updates", prep, start, nil, "core") + modules.Register("updates", prep, start, stop, "core") } func prep() error { @@ -33,8 +35,14 @@ func prep() error { return errors.New("database root is not set") } updateStoragePath = filepath.Join(dbRoot, "updates") + downloadTmpPath = filepath.Join(updateStoragePath, "tmp") - err := CheckDir(updateStoragePath) + err := utils.EnsureDirectory(updateStoragePath, 0755) + if err != nil { + return err + } + + err = utils.EnsureDirectory(downloadTmpPath, 0700) if err != nil { return err } @@ -70,34 +78,6 @@ func start() error { } func stop() error { - return os.RemoveAll(filepath.Join(updateStoragePath, "tmp")) -} - -func CheckDir(dirPath string) error { - f, err := os.Stat(dirPath) - if err == nil { - // file exists - if f.IsDir() { - return nil - } - err = os.Remove(dirPath) - if err != nil { - return fmt.Errorf("could not remove file %s to place dir: %s", dirPath, err) - } - err = os.MkdirAll(dirPath, 0755) - if err != nil { - return fmt.Errorf("could not create dir %s: %s", dirPath, err) - } - return nil - } - // file does not exist - if os.IsNotExist(err) { - err = os.MkdirAll(dirPath, 0755) - if err != nil { - return fmt.Errorf("could not create dir %s: %s", dirPath, err) - } - return nil - } - // other error - return fmt.Errorf("failed to access %s: %s", dirPath, err) + // delete download tmp dir + return os.RemoveAll(downloadTmpPath) } diff --git a/updates/updater.go b/updates/updater.go index 8dab77b2..d10984c9 100644 --- a/updates/updater.go +++ b/updates/updater.go @@ -3,7 +3,9 @@ package updates import ( "encoding/json" "errors" + "fmt" "io/ioutil" + "path" "path/filepath" "runtime" "time" @@ -22,18 +24,24 @@ func updater() { } } -func CheckForUpdates() error { +func markFileForDownload(identifier string) { + // get file + _, ok := localUpdates[identifier] + // only mark if it does not yet exist + if !ok { + localUpdates[identifier] = "loading..." + } +} - // ensure core components are updated - var err error - if runtime.GOOS == "windows" { - _, err = GetPlatformFile("pmctl/pmctl.exe") - } else { - _, err = GetPlatformFile("pmctl/pmctl") - } - if err != nil { - log.Errorf("updates: failed to mark pmctl/pmctl as used to ensure updates: %s", err) - } +func markPlatformFileForDownload(identifier string) { + // add platform prefix + identifier = path.Join(fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH), identifier) + // mark file + markFileForDownload(identifier) +} + +// CheckForUpdates checks if updates are available and downloads updates of used components. +func CheckForUpdates() (err error) { // download new index var data []byte @@ -60,6 +68,19 @@ func CheckForUpdates() error { // FIXME IN STABLE: correct log line log.Infof("updates: downloaded new update index: stable.json (alpha until we actually reach stable)") + // ensure important components are always updated + updatesLock.Lock() + if runtime.GOOS == "windows" { + markPlatformFileForDownload("control/portmaster-control.exe") + markPlatformFileForDownload("app/portmaster-app.exe") + markPlatformFileForDownload("notifier/portmaster-notifier.exe") + } else { + markPlatformFileForDownload("control/portmaster-control") + markPlatformFileForDownload("app/portmaster-app") + markPlatformFileForDownload("notifier/portmaster-notifier") + } + updatesLock.Unlock() + // update existing files log.Tracef("updates: updating existing files") updatesLock.RLock() @@ -67,16 +88,17 @@ func CheckForUpdates() error { oldVersion, ok := localUpdates[identifier] if ok && newVersion != oldVersion { + log.Tracef("updates: updating %s to %s", identifier, newVersion) filePath := GetVersionedPath(identifier, newVersion) realFilePath := filepath.Join(updateStoragePath, filePath) for tries := 0; tries < 3; tries++ { - err := fetchFile(realFilePath, filePath, tries) + err = fetchFile(realFilePath, filePath, tries) if err == nil { break } } if err != nil { - log.Warningf("failed to update %s to %s: %s", identifier, newVersion, err) + log.Warningf("updates: failed to update %s to %s: %s", identifier, newVersion, err) } }