package fastmirror import ( "bufio" _ "embed" "fmt" "log" "os" "path/filepath" "regexp" "strings" cp "github.com/otiai10/copy" ) func findSourcesFilePath() (string, error) { const sourcesListPath = "/etc/apt/sources.list" const sourcesListDPath = "/etc/apt/sources.list.d/" if isUbuntuSourcesFile(sourcesListPath) { log.Printf("Found Ubuntu sources file: %s", sourcesListPath) return sourcesListPath, nil } files, err := os.ReadDir(sourcesListDPath) if err != nil { return "", fmt.Errorf("failed to read directory %s: %v", sourcesListDPath, err) } for _, file := range files { if file.IsDir() { continue } filePath := filepath.Join(sourcesListDPath, file.Name()) if isUbuntuSourcesFile(filePath) { log.Printf("Found Ubuntu sources file: %s", filePath) return filePath, nil } } return "", fmt.Errorf("no Ubuntu sources file found") } var ubuntuMirrorURLRegex = regexp.MustCompile(`http://((?P[a-z]{2})\.)?(archive|ports)\.ubuntu\.com/(?P.*)`) func findMirrorURLInFile(filePath string) (string, error) { fileContent, err := os.ReadFile(filePath) if err != nil { return "", fmt.Errorf("failed to read file %s: %v", filePath, err) } match := ubuntuMirrorURLRegex.FindString(string(fileContent)) if match != "" { return match, nil } return "", fmt.Errorf("no URL found in file %s", filePath) } func isUbuntuSourcesFile(filePath string) bool { match, err := findMirrorURLInFile(filePath) if err != nil { log.Printf("Failed to find OEM mirror URL in file %s: %v", filePath, err) return false } log.Printf("Found OEM mirror URL in file %s: %s", filePath, match) return true } func createBackup(filePath string) error { backupPath := filePath + ".bak" if _, err := os.Stat(backupPath); err == nil { return fmt.Errorf("backup file %s already exists", backupPath) } // copy the file to the backup file if err := cp.Copy(filePath, backupPath); err != nil { return fmt.Errorf("failed to create backup file %s: %v", backupPath, err) } log.Printf("Created backup: %s", backupPath) return nil } func extractSuites(filePath string) ([]string, error) { content, err := os.ReadFile(filePath) if err != nil { return nil, fmt.Errorf("failed to read file %s: %v", filePath, err) } var suites []string scanner := bufio.NewScanner(strings.NewReader(string(content))) isModernFormat := false for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "Types:") { isModernFormat = true break } } scanner = bufio.NewScanner(strings.NewReader(string(content))) if isModernFormat { for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "Suites:") { parts := strings.Fields(line) suites = append(suites, parts[1:]...) } } } else { for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "deb ") || strings.HasPrefix(line, "deb-src ") { parts := strings.Fields(line) if len(parts) >= 4 { suites = append(suites, parts[3]) } } } } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("failed to scan file %s: %v", filePath, err) } return suites, nil } func updateSourcesFile(filePath, oldMirrorURL, newMirrorURL string) error { file, err := os.Open(filePath) if err != nil { return fmt.Errorf("failed to open sources file %s: %v", filePath, err) } file.Close() contentBytes, err := os.ReadFile(filePath) if err != nil { return fmt.Errorf("failed to read sources file %s: %v", filePath, err) } log.Printf("update: replacing %s with %s in %s", oldMirrorURL, newMirrorURL, filePath) // replace old mirror URL with new mirror URL content := string(contentBytes) content = strings.ReplaceAll(content, oldMirrorURL, newMirrorURL) if err := os.WriteFile(filePath, []byte(content), 0644); err != nil { return fmt.Errorf("failed to write sources file %s: %v", filePath, err) } log.Printf("Updated sources file: %s", filePath) return nil }