123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- package driver
-
- import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "net/url"
- "os"
- "path/filepath"
- )
-
- // settings holds pprof settings.
- type settings struct {
- // Configs holds a list of named UI configurations.
- Configs []namedConfig `json:"configs"`
- }
-
- // namedConfig associates a name with a config.
- type namedConfig struct {
- Name string `json:"name"`
- config
- }
-
- // settingsFileName returns the name of the file where settings should be saved.
- func settingsFileName() (string, error) {
- // Return "pprof/settings.json" under os.UserConfigDir().
- dir, err := os.UserConfigDir()
- if err != nil {
- return "", err
- }
- dir = filepath.Join(dir, "pprof")
- if err := os.MkdirAll(dir, 0755); err != nil {
- return "", err
- }
- return filepath.Join(dir, "settings.json"), nil
- }
-
- // readSettings reads settings from fname.
- func readSettings(fname string) (*settings, error) {
- data, err := ioutil.ReadFile(fname)
- if err != nil {
- if os.IsNotExist(err) {
- return &settings{}, nil
- }
- return nil, fmt.Errorf("could not read settings: %w", err)
- }
- settings := &settings{}
- if err := json.Unmarshal(data, settings); err != nil {
- return nil, fmt.Errorf("could not parse settings: %w", err)
- }
- for i := range settings.Configs {
- settings.Configs[i].resetTransient()
- }
- return settings, nil
- }
-
- // writeSettings saves settings to fname.
- func writeSettings(fname string, settings *settings) error {
- data, err := json.MarshalIndent(settings, "", " ")
- if err != nil {
- return fmt.Errorf("could not encode settings: %w", err)
- }
- if err := ioutil.WriteFile(fname, data, 0644); err != nil {
- return fmt.Errorf("failed to write settings: %w", err)
- }
- return nil
- }
-
- // configMenuEntry holds information for a single config menu entry.
- type configMenuEntry struct {
- Name string
- URL string
- Current bool // Is this the currently selected config?
- UserConfig bool // Is this a user-provided config?
- }
-
- // configMenu returns a list of items to add to a menu in the web UI.
- func configMenu(fname string, url url.URL) []configMenuEntry {
- // Start with system configs.
- configs := []namedConfig{{Name: "Default", config: defaultConfig()}}
- if settings, err := readSettings(fname); err == nil {
- // Add user configs.
- configs = append(configs, settings.Configs...)
- }
-
- // Convert to menu entries.
- result := make([]configMenuEntry, len(configs))
- lastMatch := -1
- for i, cfg := range configs {
- dst, changed := cfg.config.makeURL(url)
- if !changed {
- lastMatch = i
- }
- result[i] = configMenuEntry{
- Name: cfg.Name,
- URL: dst.String(),
- UserConfig: (i != 0),
- }
- }
- // Mark the last matching config as currennt
- if lastMatch >= 0 {
- result[lastMatch].Current = true
- }
- return result
- }
-
- // editSettings edits settings by applying fn to them.
- func editSettings(fname string, fn func(s *settings) error) error {
- settings, err := readSettings(fname)
- if err != nil {
- return err
- }
- if err := fn(settings); err != nil {
- return err
- }
- return writeSettings(fname, settings)
- }
-
- // setConfig saves the config specified in request to fname.
- func setConfig(fname string, request url.URL) error {
- q := request.Query()
- name := q.Get("config")
- if name == "" {
- return fmt.Errorf("invalid config name")
- }
- cfg := currentConfig()
- if err := cfg.applyURL(q); err != nil {
- return err
- }
- return editSettings(fname, func(s *settings) error {
- for i, c := range s.Configs {
- if c.Name == name {
- s.Configs[i].config = cfg
- return nil
- }
- }
- s.Configs = append(s.Configs, namedConfig{Name: name, config: cfg})
- return nil
- })
- }
-
- // removeConfig removes config from fname.
- func removeConfig(fname, config string) error {
- return editSettings(fname, func(s *settings) error {
- for i, c := range s.Configs {
- if c.Name == config {
- s.Configs = append(s.Configs[:i], s.Configs[i+1:]...)
- return nil
- }
- }
- return fmt.Errorf("config %s not found", config)
- })
- }
|