otelcollector/shared/process_utilities_linux.go (317 lines of code) (raw):

package shared import ( "fmt" "io" "log" "net/http" "os" "os/exec" "path/filepath" "strings" "sync" "time" ) func IsProcessRunning(processName string) bool { // List all processes in the current process group pid := os.Getpid() processes, err := os.ReadDir("/proc") if err != nil { fmt.Println("Error:", err) return false } for _, processDir := range processes { if processDir.IsDir() { processID := processDir.Name() _, err := os.Stat("/proc/" + processID + "/cmdline") if err == nil { cmdline, err := os.ReadFile("/proc/" + processID + "/cmdline") if err == nil { if strings.Contains(string(cmdline), processName) { // Skip the current process (this program) if processID != fmt.Sprintf("%d", pid) { return true } } } } } } return false } // SetEnvAndSourceBashrcOrPowershell sets a key-value pair as an environment variable in the .bashrc file // and sources the file to apply changes immediately. If echo is true, it calls EchoVar func SetEnvAndSourceBashrcOrPowershell(key, value string, echo bool) error { // Set the environment variable err := os.Setenv(key, value) if err != nil { fmt.Println("error in SetEnvAndSourceBashrcOrPowershell when setting key:", key, ":value:" , value, ":error:", err) return fmt.Errorf("failed to set environment variable: %v", err) } // Conditionally call EchoVar if echo { EchoVar(key, value) } if GetEnv("CCP_METRICS_ENABLED", "false") == "true" { // return if in ccp mode as no bash shell present here return nil } // Get user's home directory homeDir, err := os.UserHomeDir() if err != nil { return fmt.Errorf("failed to get user's home directory: %v", err) } // Construct the path to .bashrc bashrcPath := filepath.Join(homeDir, ".bashrc") // Check if .bashrc exists, if not, create it if _, err := os.Stat(bashrcPath); os.IsNotExist(err) { file, err := os.Create(bashrcPath) if err != nil { return fmt.Errorf("failed to create .bashrc file: %v", err) } defer file.Close() } // Open the .bashrc file for appending file, err := os.OpenFile(bashrcPath, os.O_APPEND|os.O_WRONLY, 0644) if err != nil { return fmt.Errorf("failed to open .bashrc file: %v", err) } defer file.Close() _, err = fmt.Fprintf(file, "export %s=%s\n", key, value) if err != nil { return fmt.Errorf("failed to write to .bashrc file: %v", err) } // Source the .bashrc file cmd := exec.Command("bash", "-c", "source "+bashrcPath) if err := cmd.Run(); err != nil { return fmt.Errorf("failed to source .bashrc: %v", err) } return nil } func StartCommandWithOutputFile(command string, args []string, outputFile string) (int, error) { cmd := exec.Command(command, args...) cmd.Env = append(os.Environ()) // Create file to write stdout and stderr file, err := os.Create(outputFile) if err != nil { return 0, fmt.Errorf("error creating output file: %v", err) } // Create pipes to capture stdout and stderr stdout, err := cmd.StdoutPipe() if err != nil { return 0, fmt.Errorf("error creating stdout pipe: %v", err) } stderr, err := cmd.StderrPipe() if err != nil { return 0, fmt.Errorf("error creating stderr pipe: %v", err) } // Start the command if err := cmd.Start(); err != nil { return 0, fmt.Errorf("error starting command: %v", err) } // Create a wait group to wait for goroutines var wg sync.WaitGroup wg.Add(2) // Create goroutines to continuously read and write stdout and stderr go func() { defer wg.Done() if _, err := io.Copy(file, stdout); err != nil { fmt.Printf("Error copying stdout to file: %v\n", err) } }() go func() { defer wg.Done() if _, err := io.Copy(file, stderr); err != nil { fmt.Printf("Error copying stderr to file: %v\n", err) } }() // Wait for both goroutines to finish before closing the file go func() { wg.Wait() file.Close() }() // Get the PID of the started process process_pid := cmd.Process.Pid return process_pid, nil } func StartCommand(command string, args ...string) { cmd := exec.Command(command, args...) // Set environment variables from os.Environ() cmd.Env = append(os.Environ()) // Create pipes to capture stdout and stderr stdout, err := cmd.StdoutPipe() if err != nil { fmt.Printf("Error creating stdout pipe: %v\n", err) return } stderr, err := cmd.StderrPipe() if err != nil { fmt.Printf("Error creating stderr pipe: %v\n", err) return } // Start the command err = cmd.Start() if err != nil { fmt.Printf("Error starting command: %v\n", err) return } // Create goroutines to capture and print stdout and stderr go func() { stdoutBytes, _ := io.ReadAll(io.Reader(stdout)) fmt.Print(string(stdoutBytes)) }() go func() { stderrBytes, _ := io.ReadAll(io.Reader(stderr)) fmt.Print(string(stderrBytes)) }() } func StartCommandAndWait(command string, args ...string) error { cmd := exec.Command(command, args...) // Set environment variables from os.Environ() cmd.Env = append(os.Environ()) // Create pipes to capture stdout and stderr stdout, err := cmd.StdoutPipe() if err != nil { return fmt.Errorf("error creating stdout pipe: %v", err) } stderr, err := cmd.StderrPipe() if err != nil { return fmt.Errorf("error creating stderr pipe: %v", err) } // Start the command err = cmd.Start() if err != nil { return fmt.Errorf("error starting command: %v", err) } // Create goroutines to capture and print stdout and stderr go func() { stdoutBytes, _ := io.ReadAll(io.Reader(stdout)) fmt.Print(string(stdoutBytes)) }() go func() { stderrBytes, _ := io.ReadAll(io.Reader(stderr)) fmt.Print(string(stderrBytes)) }() // Wait for the command to finish err = cmd.Wait() if err != nil { return fmt.Errorf("error waiting for command: %v", err) } return nil } func copyOutputMulti(src io.Reader, dest io.Writer, file *os.File) { // Create a multi-writer to write to both the file and os.Stdout/os.Stderr multiWriter := io.MultiWriter(dest, file) _, err := io.Copy(multiWriter, src) if err != nil { fmt.Printf("Error copying output: %v\n", err) } } func copyOutputPipe(src io.Reader, dest io.Writer) { _, err := io.Copy(dest, src) if err != nil { fmt.Printf("Error copying output: %v\n", err) } } func copyOutputFile(src io.Reader, file *os.File) { _, err := io.Copy(file, src) if err != nil { fmt.Printf("Error copying output: %v\n", err) } } func StartMetricsExtensionForOverlay(meConfigFile string) (int, error) { cmd := exec.Command("/usr/sbin/MetricsExtension", "-Logger", "File", "-LogLevel", "Info", "-LocalControlChannel", "-TokenSource", "AMCS", "-DataDirectory", "/etc/mdsd.d/config-cache/metricsextension", "-Input", "otlp_grpc_prom", "-ConfigOverridesFilePath", meConfigFile) // Set environment variables from os.Environ() cmd.Env = append(os.Environ()) // Start the command err := cmd.Start() if err != nil { return 0, fmt.Errorf("error starting MetricsExtension: %v", err) } return cmd.Process.Pid, nil } func StartMetricsExtensionWithConfigOverridesForUnderlay(configOverrides string) { cmd := exec.Command("/usr/sbin/MetricsExtension", "-Logger", "Console", "-LogLevel", "Error", "-LocalControlChannel", "-TokenSource", "AMCS", "-DataDirectory", "/etc/mdsd.d/config-cache/metricsextension", "-Input", "otlp_grpc_prom", "-ConfigOverridesFilePath", "/usr/sbin/me.config") // Create a file to store the stdoutput // metricsextension_stdout_file, err := os.Create("metricsextension_stdout.log") // if err != nil { // fmt.Printf("Error creating output file for metrics extension: %v\n", err) // return // } // // Create a file to store the stderr // metricsextension_stderr_file, err := os.Create("metricsextension_stderr.log") // if err != nil { // fmt.Printf("Error creating output file for metrics extension: %v\n", err) // return // } // Create pipes to capture stdout and stderr stdout, err := cmd.StdoutPipe() if err != nil { fmt.Printf("Error creating stdout pipe: %v\n", err) return } stderr, err := cmd.StderrPipe() if err != nil { fmt.Printf("Error creating stderr pipe: %v\n", err) return } // Goroutines to copy stdout and stderr to parent process // Copy output to only stdout & stderr go copyOutputPipe(stdout, os.Stdout) go copyOutputPipe(stderr, os.Stderr) // Copy output to both stdout & stderr and file // go copyOutputMulti(stdout, os.Stdout, metricsextension_stdout_file) // go copyOutputMulti(stderr, os.Stderr, metricsextension_stderr_file) // Copy output only to file // go copyOutputFile(stdout, metricsextension_stdout_file) // go copyOutputFile(stderr, metricsextension_stderr_file) // Start the command err = cmd.Start() if err != nil { fmt.Printf("Error starting MetricsExtension: %v\n", err) return } } func StartMA() { fmt.Println("Should never reach here, defining function since main.go expects it") } func StartMdsdForOverlay() { mdsdLog := os.Getenv("MDSD_LOG") if mdsdLog == "" { fmt.Println("MDSD_LOG environment variable is not set") return } cmd := exec.Command("/usr/sbin/mdsd", "-a", "-A", "-e", mdsdLog+"/mdsd.err", "-w", mdsdLog+"/mdsd.warn", "-o", mdsdLog+"/mdsd.info", "-q", mdsdLog+"/mdsd.qos") // Redirect stderr to /dev/null cmd.Stderr = nil // Start the command err := cmd.Start() if err != nil { fmt.Printf("Error starting mdsd: %v\n", err) return } } func StartMdsdForUnderlay() { cmd := exec.Command("/usr/sbin/mdsd", "-a", "-A", "-D") // // Create a file to store the stdoutput // mdsd_stdout_file, err := os.Create("mdsd_stdout.log") // if err != nil { // fmt.Printf("Error creating output file for mdsd: %v\n", err) // return // } // // Create a file to store the stderr // mdsd_stderr_file, err := os.Create("mdsd_stderr.log") // if err != nil { // fmt.Printf("Error creating output file for mdsd: %v\n", err) // return // } // Create pipes to capture stdout and stderr stdout, err := cmd.StdoutPipe() if err != nil { fmt.Printf("Error creating stdout pipe: %v\n", err) return } stderr, err := cmd.StderrPipe() if err != nil { fmt.Printf("Error creating stderr pipe: %v\n", err) return } // Goroutines to copy stdout and stderr to parent process // Copy output to only stdout and stderr go copyOutputPipe(stdout, os.Stdout) go copyOutputPipe(stderr, os.Stderr) // Copy output to both stdout and file // go copyOutputMulti(stdout, os.Stdout, mdsd_stdout_file) // go copyOutputMulti(stderr, os.Stderr, mdsd_stderr_file) // Copy output only to file // go copyOutputFile(stdout, mdsd_stdout_file) // go copyOutputFile(stderr, mdsd_stderr_file) // Start the command err = cmd.Start() if err != nil { fmt.Printf("Error starting mdsd: %v\n", err) return } } func StartCronDaemon() { cmd := exec.Command("/usr/sbin/crond", "-n", "-s") if err := cmd.Start(); err != nil { log.Fatal(err) } } func WaitForTokenAdapter(ccpMetricsEnabled string) { tokenAdapterWaitSecs := 60 if ccpMetricsEnabled == "true" { tokenAdapterWaitSecs = 20 } waitedSecsSoFar := 1 var resp *http.Response var err error client := &http.Client{Timeout: time.Duration(2) * time.Second} req, err := http.NewRequest("GET", "http://localhost:9999/healthz", nil) if err != nil { log.Printf("Unable to create http request for the healthz endpoint") return } for { if waitedSecsSoFar > tokenAdapterWaitSecs { if resp, err = client.Do(req); err != nil { log.Printf("giving up waiting for token adapter to become healthy after %d secs\n", waitedSecsSoFar) log.Printf("export tokenadapterUnhealthyAfterSecs=%d\n", waitedSecsSoFar) break } } else { log.Printf("checking health of token adapter after %d secs\n", waitedSecsSoFar) resp, err = client.Do(req) if err == nil && resp.StatusCode == http.StatusOK { log.Printf("found token adapter to be healthy after %d secs\n", waitedSecsSoFar) log.Printf("export tokenadapterHealthyAfterSecs=%d\n", waitedSecsSoFar) break } } time.Sleep(1 * time.Second) waitedSecsSoFar++ } if resp != nil && resp.Body != nil { defer resp.Body.Close() } } func StartFluentBit(fluentBitConfigFile string) { fmt.Println("Starting fluent-bit") if err := os.Mkdir("/opt/microsoft/fluent-bit", 0755); err != nil && !os.IsExist(err) { log.Fatalf("Error creating directory: %v\n", err) } logFile, err := os.Create("/opt/microsoft/fluent-bit/fluent-bit-out-appinsights-runtime.log") if err != nil { log.Fatalf("Error creating log file: %v\n", err) } defer logFile.Close() fluentBitCmd := exec.Command("fluent-bit", "-c", fluentBitConfigFile, "-e", "/opt/fluent-bit/bin/out_appinsights.so") fluentBitCmd.Stdout = os.Stdout fluentBitCmd.Stderr = os.Stderr if err := fluentBitCmd.Start(); err != nil { log.Fatalf("Error starting fluent-bit: %v\n", err) } }