github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/util/configv3/write_config.go (about)

     1  package configv3
     2  
     3  import (
     4  	"encoding/json"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/signal"
     8  	"syscall"
     9  	"time"
    10  )
    11  
    12  // WriteConfig creates the .cf directory and then writes the config.json. The
    13  // location of .cf directory is written in the same way LoadConfig reads .cf
    14  // directory.
    15  func (c *Config) WriteConfig() error {
    16  	rawConfig, err := json.MarshalIndent(c.ConfigFile, "", "  ")
    17  	if err != nil {
    18  		return err
    19  	}
    20  
    21  	dir := configDirectory()
    22  	err = os.MkdirAll(dir, 0700)
    23  	if err != nil {
    24  		return err
    25  	}
    26  
    27  	// Developer Note: The following is untested! Change at your own risk.
    28  	// Setup notifications of termination signals to channel sig, create a process to
    29  	// watch for these signals so we can remove transient config temp files.
    30  	sig := make(chan os.Signal, 10)
    31  	signal.Notify(sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, os.Interrupt)
    32  	defer signal.Stop(sig)
    33  
    34  	tempConfigFile, err := ioutil.TempFile(dir, "temp-config")
    35  	if err != nil {
    36  		return err
    37  	}
    38  	tempConfigFile.Close()
    39  	tempConfigFileName := tempConfigFile.Name()
    40  
    41  	go catchSignal(sig, tempConfigFileName)
    42  
    43  	err = ioutil.WriteFile(tempConfigFileName, rawConfig, 0600)
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	for i := 0; i < 5; i++ {
    49  		if err := os.Rename(tempConfigFileName, ConfigFilePath()); err == nil {
    50  			return nil
    51  		}
    52  
    53  		time.Sleep(50 * time.Millisecond)
    54  	}
    55  
    56  	return os.Rename(tempConfigFileName, ConfigFilePath())
    57  }
    58  
    59  // catchSignal tries to catch SIGHUP, SIGINT, SIGKILL, SIGQUIT and SIGTERM, and
    60  // Interrupt for removing temporarily created config files before the program
    61  // ends.  Note:  we cannot intercept a `kill -9`, so a well-timed `kill -9`
    62  // will allow a temp config file to linger.
    63  func catchSignal(sig chan os.Signal, tempConfigFileName string) {
    64  	<-sig
    65  	_ = os.Remove(tempConfigFileName)
    66  	os.Exit(2)
    67  }