|
@@ -20,22 +20,30 @@
|
|
|
package main
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
|
+ "encoding/json"
|
|
|
"flag"
|
|
"flag"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
|
|
+ "io"
|
|
|
"io/ioutil"
|
|
"io/ioutil"
|
|
|
"os"
|
|
"os"
|
|
|
"strconv"
|
|
"strconv"
|
|
|
"strings"
|
|
"strings"
|
|
|
|
|
+ "time"
|
|
|
|
|
|
|
|
|
|
+ "github.com/Psiphon-Inc/panicwrap"
|
|
|
|
|
+ "github.com/Psiphon-Inc/rotate-safe-writer"
|
|
|
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
|
|
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
|
|
|
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/server"
|
|
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/server"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+var loadedConfigJSON []byte
|
|
|
|
|
+
|
|
|
func main() {
|
|
func main() {
|
|
|
|
|
|
|
|
var generateTrafficRulesFilename string
|
|
var generateTrafficRulesFilename string
|
|
|
var generateServerEntryFilename string
|
|
var generateServerEntryFilename string
|
|
|
var generateLogFilename string
|
|
var generateLogFilename string
|
|
|
|
|
+ var generatePanicLogFilename string
|
|
|
var generateServerIPaddress string
|
|
var generateServerIPaddress string
|
|
|
var generateServerNetworkInterface string
|
|
var generateServerNetworkInterface string
|
|
|
var generateWebServerPort int
|
|
var generateWebServerPort int
|
|
@@ -60,6 +68,12 @@ func main() {
|
|
|
"",
|
|
"",
|
|
|
"set application log file name and path; blank for stderr")
|
|
"set application log file name and path; blank for stderr")
|
|
|
|
|
|
|
|
|
|
+ flag.StringVar(
|
|
|
|
|
+ &generatePanicLogFilename,
|
|
|
|
|
+ "panicLogFilename",
|
|
|
|
|
+ "",
|
|
|
|
|
+ "set application log file name and path for recording un-recovered panics; blank for stderr")
|
|
|
|
|
+
|
|
|
flag.StringVar(
|
|
flag.StringVar(
|
|
|
&generateServerIPaddress,
|
|
&generateServerIPaddress,
|
|
|
"ipaddress",
|
|
"ipaddress",
|
|
@@ -135,6 +149,7 @@ func main() {
|
|
|
server.GenerateConfig(
|
|
server.GenerateConfig(
|
|
|
&server.GenerateConfigParams{
|
|
&server.GenerateConfigParams{
|
|
|
LogFilename: generateLogFilename,
|
|
LogFilename: generateLogFilename,
|
|
|
|
|
+ PanicLogFilename: generatePanicLogFilename,
|
|
|
ServerIPAddress: serverIPaddress,
|
|
ServerIPAddress: serverIPaddress,
|
|
|
EnableSSHAPIRequests: true,
|
|
EnableSSHAPIRequests: true,
|
|
|
WebServerPort: generateWebServerPort,
|
|
WebServerPort: generateWebServerPort,
|
|
@@ -172,6 +187,22 @@ func main() {
|
|
|
os.Exit(1)
|
|
os.Exit(1)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ loadedConfigJSON = configJSON
|
|
|
|
|
+
|
|
|
|
|
+ // Unhandled panic wrapper. Logs it, then re-executes the current executable
|
|
|
|
|
+ exitStatus, exitErr := panicwrap.BasicWrap(panicHandler)
|
|
|
|
|
+ if exitErr != nil {
|
|
|
|
|
+ fmt.Printf("failed to set up the panic wrapper: %s\n", err)
|
|
|
|
|
+ os.Exit(1)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If exitStatus >= 0, then we're the parent process and the panicwrap
|
|
|
|
|
+ // re-executed ourselves and completed. Just exit with the proper status.
|
|
|
|
|
+ if exitStatus >= 0 {
|
|
|
|
|
+ os.Exit(exitStatus)
|
|
|
|
|
+ }
|
|
|
|
|
+ // Otherwise, exitStatus < 0 means we're the child. Continue executing as normal
|
|
|
|
|
+
|
|
|
err = server.RunServices(configJSON)
|
|
err = server.RunServices(configJSON)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
fmt.Printf("run failed: %s\n", err)
|
|
fmt.Printf("run failed: %s\n", err)
|
|
@@ -190,3 +221,44 @@ func (list *stringListFlag) Set(flagValue string) error {
|
|
|
*list = append(*list, flagValue)
|
|
*list = append(*list, flagValue)
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+func panicHandler(output string) {
|
|
|
|
|
+ if len(loadedConfigJSON) > 0 {
|
|
|
|
|
+ config, err := server.LoadConfig([]byte(loadedConfigJSON))
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ fmt.Errorf("error parsing configuration file: %s", err)
|
|
|
|
|
+ os.Exit(1)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ logEvent := make(map[string]string)
|
|
|
|
|
+ logEvent["host_id"] = config.HostID
|
|
|
|
|
+ logEvent["build_rev"] = common.GetBuildInfo().BuildRev
|
|
|
|
|
+ logEvent["timestamp"] = time.Now().Format(time.RFC3339)
|
|
|
|
|
+ logEvent["event_name"] = "panic"
|
|
|
|
|
+ logEvent["panic"] = output
|
|
|
|
|
+
|
|
|
|
|
+ var jsonWriter io.Writer
|
|
|
|
|
+ if config.PanicLogFilename != "" {
|
|
|
|
|
+ panicLog, err := rotate.NewRotatableFileWriter(config.PanicLogFilename, 0666)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ fmt.Printf("unable to set panic log output: %s\n", err)
|
|
|
|
|
+ os.Exit(1)
|
|
|
|
|
+ }
|
|
|
|
|
+ jsonWriter = panicLog
|
|
|
|
|
+ } else {
|
|
|
|
|
+ jsonWriter = os.Stderr
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ enc := json.NewEncoder(jsonWriter)
|
|
|
|
|
+ err = enc.Encode(logEvent)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ fmt.Printf("unable to serialize panic message to JSON: %s\n", err)
|
|
|
|
|
+ os.Exit(1)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ fmt.Errorf("no configuration JSON was loaded, cannot continue")
|
|
|
|
|
+ os.Exit(1)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ os.Exit(1)
|
|
|
|
|
+}
|