// Set the ``tlsconf.Config`` variable. func Init() { path := runtime.AmpifyRoot + "/environ/local/share/cacerts/ca.cert" var err error Config, err = GenConfig(path) if err != nil { runtime.Error("ERROR: Couldn't load %s: %s\n", path, err) } }
func (op *OptionParser) ParseConfig(filename string, args []string) (err os.Error) { data, err := yaml.ParseFile(filename) if err != nil { return err } for config, opt := range op.config2options { if opt.defined { continue } value, ok := data[config] if !ok { if opt.requiredConfig { runtime.Error("%s: error: required: %s", args[0], opt) } else { continue } } if opt.valueType == "bool" { if value == "true" || value == "on" || value == "yes" { *opt.boolValue = true } else if value == "false" || value == "off" || value == "no" { *opt.boolValue = false } else { runtime.Error("%s: error: invalid boolean value for %s: %q\n", args[0], config, value) } } else if opt.valueType == "string" { *opt.stringValue = value } else if opt.valueType == "int" { intValue, err := strconv.Atoi(value) if err != nil { runtime.Error("%s: error: couldn't convert the %s value %q to an integer\n", args[0], config, value) } *opt.intValue = intValue } } return nil }
func main() { opts := optparse.Parser("Usage: hubproxy [options]\n", "hubproxy 0.0.0") port := opts.Int([]string{"-p", "--port"}, 8010, "the port number to use [default: 8010]") host := opts.String([]string{"--host"}, "localhost", "the host to bind to") remote := opts.String([]string{"-r", "--remote"}, "ampcentral.appspot.com", "the remote host to connect to [default: ampcentral.appspot.com]") debug := opts.Bool([]string{"--debug"}, false, "enable debug mode") os.Args[0] = "hubproxy" args := opts.Parse(os.Args) if len(args) >= 1 { if args[0] == "help" { opts.PrintUsage() runtime.Exit(1) } } // Initialise the Ampify runtime -- which will run hubproxy on multiple // processors if possible. runtime.Init() // Initialise the TLS config. tlsconf.Init() debugMode = *debug remoteHost = *remote remoteAddr = *remote + ":443" addr := fmt.Sprintf("%s:%d", *host, *port) listener, err := net.Listen("tcp", addr) if err != nil { runtime.Error("Cannot listen on %s: %v\n", addr, err) } fmt.Printf("Running hubproxy with %d CPUs on %s\n", runtime.CPUCount, addr) proxy := &Proxy{} http.Serve(listener, proxy) }
func runProcess(amp, cmd, path, config string, console bool, quit chan bool) { var files []*os.File if console { files = []*os.File{nil, os.Stdout, os.Stderr} } else { files = []*os.File{nil, nil, nil} } logging.Info("Running: amp %s %s", cmd, config) process, err := os.StartProcess( amp, []string{"amp", cmd, config}, &os.ProcAttr{ Dir: path, Env: os.Environ(), Files: files, }) if err != nil { runtime.StandardError(err) } waitmsg, err := process.Wait(0) if err != nil { runtime.StandardError(err) } if waitmsg.ExitStatus() != 0 { runtime.Error("ERROR: Got %s when running `amp %s %s`\n", waitmsg, cmd, config) } quit <- true }
func main() { // Define the options for the command line and config file options parser. opts := optparse.Parser( "Usage: live-server <config.yaml> [options]\n", "live-server 0.0.1") debug := opts.Bool([]string{"-d", "--debug"}, false, "enable debug mode") genConfig := opts.Bool([]string{"-g", "--gen-config"}, false, "show the default yaml config") frontendHost := opts.StringConfig("frontend-host", "", "the host to bind the HTTPS Frontends to") frontendPort := opts.IntConfig("frontend-port", 9040, "the base port for the HTTPS Frontends [9040]") officialHost := opts.StringConfig("offficial-host", "", "the official public host for the HTTPS Frontends") primaryHosts := opts.StringConfig("primary-hosts", "", "limit the primary HTTPS Frontend to the specified host pattern") primaryCert := opts.StringConfig("primary-cert", "cert/primary.cert", "the path to the primary host's TLS certificate [cert/primary.cert]") primaryKey := opts.StringConfig("primary-key", "cert/primary.key", "the path to the primary host's TLS key [cert/primary.key]") noSecondary := opts.BoolConfig("no-secondary", false, "disable the secondary HTTPS Frontend [false]") secondaryHosts := opts.StringConfig("secondary-hosts", "", "limit the secondary HTTPS Frontend to the specified host pattern") secondaryCert := opts.StringConfig("secondary-cert", "cert/secondary.cert", "the path to the secondary host's TLS certificate [cert/secondary.cert]") secondaryKey := opts.StringConfig("secondary-key", "cert/secondary.key", "the path to the secondary host's TLS key [cert/secondary.key]") errorDirectory := opts.StringConfig("error-dir", "error", "the path to the HTTP error files directory [error]") logDirectory := opts.StringConfig("log-dir", "log", "the path to the log directory [log]") runDirectory := opts.StringConfig("run-dir", "run", "the path to the run directory to store locks, pid files, etc. [run]") staticDirectory := opts.StringConfig("static-dir", "www", "the path to the static files directory [www]") staticMaxAge := opts.IntConfig("static-max-age", 86400, "max-age cache header value when serving the static files [86400]") noLivequery := opts.BoolConfig("no-livequery", false, "disable the LiveQuery node and WebSocket/Comet support [false]") websocketPrefix := opts.StringConfig("websocket-prefix", "/.live/ws", "URL path prefix for WebSocket requests [/.live/ws]") cometPrefix := opts.StringConfig("comet-prefix", "/.live/poll", "URL path prefix for Comet requests [/.live/poll]") livequeryHost := opts.StringConfig("livequery-host", "", "the host to bind the LiveQuery node to") livequeryPort := opts.IntConfig("livequery-port", 9050, "the port (both UDP and TCP) to bind the LiveQuery node to [9050]") livequeryExpiry := opts.IntConfig("livequery-expiry", 40, "maximum number of seconds a LiveQuery subscription is valid [40]") cookieKeyPath := opts.StringConfig("cookie-key", "cert/cookie.key", "the path to the file containing the key used to sign cookies [cert/cookie.key]") cookieName := opts.StringConfig("cookie-name", "user", "the property name of the cookie containing the user id [user]") acceptors := opts.StringConfig("acceptor-nodes", "localhost:9060", "comma-separated addresses of Acceptor nodes [localhost:9060]") acceptorKeyPath := opts.StringConfig("acceptor-key", "cert/acceptor.key", "the path to the file containing the Acceptor secret key [cert/acceptor.key]") runAcceptor := opts.BoolConfig("run-as-acceptor", false, "run as an Acceptor node [false]") acceptorIndex := opts.IntConfig("acceptor-index", 0, "this node's index in the Acceptor nodes address list [0]") leaseExpiry := opts.IntConfig("lease-expiry", 7, "maximum number of seconds a lease from an Acceptor node is valid [7]") noRedirect := opts.BoolConfig("no-redirect", false, "disable the HTTP Redirector [false]") httpHost := opts.StringConfig("http-host", "", "the host to bind the HTTP Redirector to") httpPort := opts.IntConfig("http-port", 9080, "the port to bind the HTTP Redirector to [9080]") redirectURL := opts.StringConfig("redirect-url", "", "the URL that the HTTP Redirector redirects to") pingPath := opts.StringConfig("ping-path", "/.ping", `URL path for a "ping" request [/.ping]`) enableHSTS := opts.BoolConfig("enable-hsts", false, "enable HTTP Strict Transport Security (HSTS) on redirects [false]") hstsMaxAge := opts.IntConfig("hsts-max-age", 50000000, "max-age value of HSTS in number of seconds [50000000]") upstreamHost := opts.StringConfig("upstream-host", "localhost", "the upstream host to connect to [localhost]") upstreamPort := opts.IntConfig("upstream-port", 8080, "the upstream port to connect to [8080]") upstreamTLS := opts.BoolConfig("upstream-tls", false, "use TLS when connecting to upstream [false]") logRotate := opts.StringConfig("log-rotate", "never", "specify one of 'hourly', 'daily' or 'never' [never]") noConsoleLog := opts.BoolConfig("no-console-log", false, "disable server requests being logged to the console [false]") maintenanceMode := opts.BoolConfig("maintenance", false, "start up in maintenance mode [false]") extraConfig := opts.StringConfig("extra-config", "", "path to a YAML config file with additional options") // Parse the command line options. os.Args[0] = "live-server" args := opts.Parse(os.Args) // Print the default YAML config file if the ``-g`` flag was specified. if *genConfig { opts.PrintDefaultConfigFile("live-server") runtime.Exit(0) } // Setup the console logger early. if !*noConsoleLog { log.AddConsoleLogger() log.ConsoleFilters["ls"] = func(items []interface{}) (bool, []interface{}) { return true, items[2 : len(items)-2] } } // Set the debug mode flag if the ``-d`` flag was specified. debugMode = *debug var instanceDirectory string var configPath string var err error // Assume the parent directory of the config as the instance directory. if len(args) >= 1 { if args[0] == "help" { opts.PrintUsage() runtime.Exit(0) } configPath, err = filepath.Abs(filepath.Clean(args[0])) if err != nil { runtime.StandardError(err) } err = opts.ParseConfig(configPath, os.Args) if err != nil { runtime.StandardError(err) } instanceDirectory, _ = filepath.Split(configPath) } else { opts.PrintUsage() runtime.Exit(0) } // Load the extra config file with additional options if one has been // specified. if *extraConfig != "" { extraConfigPath, err := filepath.Abs(filepath.Clean(*extraConfig)) if err != nil { runtime.StandardError(err) } extraConfigPath = runtime.JoinPath(instanceDirectory, extraConfigPath) err = opts.ParseConfig(extraConfigPath, os.Args) if err != nil { runtime.StandardError(err) } } // Create the log directory if it doesn't exist. logPath := runtime.JoinPath(instanceDirectory, *logDirectory) err = os.MkdirAll(logPath, 0755) if err != nil { runtime.StandardError(err) } // Create the run directory if it doesn't exist. runPath := runtime.JoinPath(instanceDirectory, *runDirectory) err = os.MkdirAll(runPath, 0755) if err != nil { runtime.StandardError(err) } // Initialise the runtime -- which will run ``live-server`` on multiple // processors if possible. runtime.Init() // Handle running as an Acceptor node if ``--run-as-acceptor`` was // specified. if *runAcceptor { // Exit if the `--acceptor-index`` is negative. if *acceptorIndex < 0 { runtime.Error("The --acceptor-index cannot be negative.") } var index int var selfAddress string var acceptorNodes []string // Generate a list of all the acceptor node addresses and exit if we // couldn't find the address four ourselves at the given index. for _, acceptor := range strings.Split(*acceptors, ",") { acceptor = strings.TrimSpace(acceptor) if acceptor != "" { if index == *acceptorIndex { selfAddress = acceptor } else { acceptorNodes = append(acceptorNodes, acceptor) } } index += 1 } if selfAddress == "" { runtime.Error("Couldn't determine the address for the acceptor.") } // Initialise the process-related resources. runtime.InitProcess(fmt.Sprintf("acceptor-%d", *acceptorIndex), runPath) return } // Initialise the process-related resources. runtime.InitProcess("live-server", runPath) // Ensure that the directory containing static files exists. staticPath := runtime.JoinPath(instanceDirectory, *staticDirectory) dirInfo, err := os.Stat(staticPath) if err == nil { if !dirInfo.IsDir() { runtime.Error("Static path %q is not a directory", staticPath) } } else { runtime.StandardError(err) } // Load up all static files into a mapping. staticFiles := make(map[string]*StaticFile) getFiles(staticPath, staticFiles, "") // Pre-format the Cache-Control header for static files. staticCache := fmt.Sprintf("public, max-age=%d", *staticMaxAge) staticMaxAge64 := time.Duration(*staticMaxAge) // Exit if the directory containing the 50x.html files isn't present. errorPath := runtime.JoinPath(instanceDirectory, *errorDirectory) dirInfo, err = os.Stat(errorPath) if err == nil { if !dirInfo.IsDir() { runtime.Error("Error path %q is not a directory", errorPath) } } else { runtime.StandardError(err) } // Load the content for the HTTP ``400``, ``500``, ``502`` and ``503`` // errors. error400, error400Length = getErrorInfo(errorPath, "400.html") error500, error500Length = getErrorInfo(errorPath, "500.html") error502, error502Length = getErrorInfo(errorPath, "502.html") error503, error503Length = getErrorInfo(errorPath, "503.html") // Initialise the TLS config. tlsconf.Init() // Setup the file loggers. var rotate int switch *logRotate { case "daily": rotate = log.RotateDaily case "hourly": rotate = log.RotateHourly case "never": rotate = log.RotateNever default: runtime.Error("Unknown log rotation format %q", *logRotate) } _, err = log.AddFileLogger("live-server", logPath, rotate, log.InfoLog) if err != nil { runtime.Error("Couldn't initialise logfile: %s", err) } _, err = log.AddFileLogger("error", logPath, rotate, log.ErrorLog) if err != nil { runtime.Error("Couldn't initialise logfile: %s", err) } var liveMode bool // Setup the live support as long as it hasn't been disabled. if !*noLivequery { go handleLiveMessages() acceptorKey, err = ioutil.ReadFile(runtime.JoinPath(instanceDirectory, *acceptorKeyPath)) if err != nil { runtime.StandardError(err) } cookieKey, err = ioutil.ReadFile(runtime.JoinPath(instanceDirectory, *cookieKeyPath)) if err != nil { runtime.StandardError(err) } liveMode = true _ = *livequeryHost _ = *livequeryPort _ = *cookieName _ = *leaseExpiry livequeryTimeout = (time.Duration(*livequeryExpiry) / 2) * 1000000000 } // Create a container for the Frontend instances. frontends := make([]*Frontend, 0) // Create a channel which is used to toggle the state of the live-server's // maintenance mode based on process signals. maintenanceChannel := make(chan bool, 1) // Fork a goroutine which toggles the maintenance mode in a single place and // thus ensures "thread safety". go func() { for { enabledState := <-maintenanceChannel for _, frontend := range frontends { if enabledState { frontend.maintenanceMode = true } else { frontend.maintenanceMode = false } } } }() // Register the signal handlers for SIGUSR1 and SIGUSR2. runtime.SignalHandlers[os.SIGUSR1] = func() { maintenanceChannel <- true } runtime.SignalHandlers[os.SIGUSR2] = func() { maintenanceChannel <- false } // Let the user know how many CPUs we're currently running on. fmt.Printf("Running live-server with %d CPUs:\n", runtime.CPUCount) // If ``--public-address`` hasn't been specified, generate it from the given // frontend host and base port values -- assuming ``localhost`` for a blank // host. publicHost := *officialHost if publicHost == "" { if *frontendHost == "" { publicHost = fmt.Sprintf("localhost:%d", *frontendPort) } else { publicHost = fmt.Sprintf("%s:%d", *frontendHost, *frontendPort) } } // Setup and run the primary HTTPS Frontend. frontends = append(frontends, initFrontend("primary", *frontendHost, *frontendPort, publicHost, *primaryHosts, *primaryCert, *primaryKey, *cometPrefix, *websocketPrefix, instanceDirectory, *upstreamHost, *upstreamPort, *upstreamTLS, *maintenanceMode, liveMode, staticCache, staticFiles, staticMaxAge64)) // Setup and run the secondary HTTPS Frontend. if !*noSecondary { frontends = append(frontends, initFrontend("secondary", *frontendHost, *frontendPort+1, publicHost, *secondaryHosts, *secondaryCert, *secondaryKey, *cometPrefix, *websocketPrefix, instanceDirectory, *upstreamHost, *upstreamPort, *upstreamTLS, *maintenanceMode, liveMode, staticCache, staticFiles, staticMaxAge64)) } // Enter a wait loop if the HTTP Redirector has been disabled. if *noRedirect { loopForever := make(chan bool, 1) <-loopForever } // Otherwise, setup the HTTP Redirector. if *httpHost == "" { *httpHost = "localhost" } if *redirectURL == "" { *redirectURL = "https://" + publicHost } httpAddr := fmt.Sprintf("%s:%d", *httpHost, *httpPort) httpListener, err := net.Listen("tcp", httpAddr) if err != nil { runtime.Error("Cannot listen on %s: %v", httpAddr, err) } hsts := "" if *enableHSTS { hsts = fmt.Sprintf("max-age=%d", *hstsMaxAge) } redirector := &Redirector{ hsts: hsts, pingPath: *pingPath, url: *redirectURL, } // Start a goroutine which runs the HTTP redirector. go func() { err = http.Serve(httpListener, redirector) if err != nil { runtime.Error("Couldn't serve HTTP Redirector: %s", err) } }() fmt.Printf("* HTTP Redirector running on http://%s:%d -> %s\n", *httpHost, *httpPort, *redirectURL) // Enter the wait loop for the process to be killed. loopForever := make(chan bool, 1) <-loopForever }
// The ``initFrontend`` utility function abstracts away the various checks and // steps involved in setting up and running a new HTTPS Frontend. func initFrontend(status, host string, port int, officialHost, validAddress, cert, key, cometPrefix, websocketPrefix, instanceDirectory, upstreamHost string, upstreamPort int, upstreamTLS, maintenanceMode, liveMode bool, staticCache string, staticFiles map[string]*StaticFile, staticMaxAge time.Duration) *Frontend { var err error // Exit if the config values for the paths of the server's certificate or // key haven't been specified. if cert == "" { runtime.Error("The %s-cert config value hasn't been specified.", status) } if key == "" { runtime.Error("The %s-key config value hasn't been specified.", status) } // Initialise a fresh TLS Config. tlsConfig := &tls.Config{ NextProtos: []string{"http/1.1"}, Rand: rand.Reader, Time: time.Now, } // Load the certificate and private key into the TLS config. certPath := runtime.JoinPath(instanceDirectory, cert) keyPath := runtime.JoinPath(instanceDirectory, key) tlsConfig.Certificates = make([]tls.Certificate, 1) tlsConfig.Certificates[0], err = tls.LoadX509KeyPair(certPath, keyPath) if err != nil { runtime.Error("Couldn't load %s certificate/key pair: %s", status, err) } // Instantiate the associated variables and listener for the HTTPS Frontend. upstreamAddr := fmt.Sprintf("%s:%d", upstreamHost, upstreamPort) frontendAddr := fmt.Sprintf("%s:%d", host, port) frontendConn, err := net.Listen("tcp", frontendAddr) if err != nil { runtime.Error("Cannot listen on %s: %v", frontendAddr, err) } frontendListener := tls.NewListener(frontendConn, tlsConfig) // Compute the variables related to detecting valid hosts. var validWildcard bool if strings.HasPrefix(validAddress, "*.") { validAddress = validAddress[2:] validWildcard = true } // Compute the variables related to redirects. redirectURL := "https://" + officialHost redirectHTML := []byte(fmt.Sprintf(redirectHTML, redirectURL)) // Instantiate a ``Frontend`` object for use by the HTTPS Frontend. frontend := &Frontend{ cometPrefix: cometPrefix, liveMode: liveMode, maintenanceMode: maintenanceMode, redirectHTML: redirectHTML, redirectURL: redirectURL, staticCache: staticCache, staticFiles: staticFiles, staticMaxAge: staticMaxAge, upstreamAddr: upstreamAddr, upstreamHost: upstreamHost, upstreamTLS: upstreamTLS, validAddress: validAddress, validWildcard: validWildcard, websocketPrefix: websocketPrefix, } // Start the HTTPS Frontend. go func() { err = http.Serve(frontendListener, frontend) if err != nil { runtime.Error("Couldn't serve %s HTTPS Frontend: %s", status, err) } }() var frontendURL string if host == "" { frontendURL = fmt.Sprintf("https://localhost:%d", port) } else { frontendURL = fmt.Sprintf("https://%s:%d", host, port) } fmt.Printf("* HTTPS Frontend %s running on %s\n", status, frontendURL) return frontend }
func main() { opts := optparse.Parser( "Usage: ampzero </path/to/instance/directory> [options]\n", "ampzero 0.0.0") debug := opts.Bool([]string{"-d", "--debug"}, false, "enable debug mode") frontendHost := opts.StringConfig("frontend-host", "", "the host to bind the Frontend Server to") frontendPort := opts.IntConfig("frontend-port", 9040, "the port to bind the Frontend Server to [default: 9040]") frontendTLS := opts.BoolConfig("frontend-tls", false, "use TLS (HTTPS) for the Frontend Server [default: false]") certFile := opts.StringConfig("cert-file", "cert/frontend.cert", "the path to the TLS certificate [default: cert/frontend.cert]") keyFile := opts.StringConfig("key-file", "cert/frontend.key", "the path to the TLS key [default: cert/frontend.key]") officialHost := opts.StringConfig("official-host", "", "if set, limit the Frontend Server to the specified host") noRedirect := opts.BoolConfig("no-redirect", false, "disable the HTTP Redirector [default: false]") httpHost := opts.StringConfig("http-host", "", "the host to bind the HTTP Redirector to") httpPort := opts.IntConfig("http-port", 9080, "the port to bind the HTTP Redirector to [default: 9080]") redirectURL := opts.StringConfig("redirect-url", "", "the URL that the HTTP Redirector redirects to") gaeHost := opts.StringConfig("gae-host", "localhost", "the App Engine host to connect to [default: localhost]") gaePort := opts.IntConfig("gae-port", 8080, "the App Engine port to connect to [default: 8080]") gaeTLS := opts.BoolConfig("gae-tls", false, "use TLS when connecting to App Engine [default: false]") logRotate := opts.StringConfig("log-rotate", "never", "specify one of 'hourly', 'daily' or 'never' [default: never]") noConsoleLog := opts.BoolConfig("no-console-log", false, "disable logging to stdout/stderr [default: false]") os.Args[0] = "ampzero" args := opts.Parse(os.Args) var instanceDirectory string if len(args) >= 1 { if args[0] == "help" { opts.PrintUsage() runtime.Exit(0) } instanceDirectory = path.Clean(args[0]) } else { opts.PrintUsage() runtime.Exit(0) } rootInfo, err := os.Stat(instanceDirectory) if err == nil { if !rootInfo.IsDirectory() { runtime.Error("ERROR: %q is not a directory\n", instanceDirectory) } } else { runtime.Error("ERROR: %s\n", err) } configPath := path.Join(instanceDirectory, "ampzero.yaml") _, err = os.Stat(configPath) if err == nil { err = opts.ParseConfig(configPath, os.Args) if err != nil { runtime.Error("ERROR: %s\n", err) } } logPath := path.Join(instanceDirectory, "log") err = os.MkdirAll(logPath, 0755) if err != nil { runtime.Error("ERROR: %s\n", err) } runPath := path.Join(instanceDirectory, "run") err = os.MkdirAll(runPath, 0755) if err != nil { runtime.Error("ERROR: %s\n", err) } _, err = runtime.GetLock(runPath, "ampzero") if err != nil { runtime.Error("ERROR: Couldn't successfully acquire a process lock:\n\n\t%s\n\n", err) } go runtime.CreatePidFile(path.Join(runPath, "ampzero.pid")) if *frontendTLS { var exitProcess bool if len(*certFile) == 0 { fmt.Printf("ERROR: The cert-file config value hasn't been specified.\n") exitProcess = true } if len(*keyFile) == 0 { fmt.Printf("ERROR: The key-file config value hasn't been specified.\n") exitProcess = true } if exitProcess { runtime.Exit(1) } } // Initialise the Ampify runtime -- which will run ``ampzero`` on multiple // processors if possible. runtime.Init() // Initialise the TLS config. tlsconf.Init() debugMode = *debug gaeAddr := fmt.Sprintf("%s:%d", *gaeHost, *gaePort) frontendAddr := fmt.Sprintf("%s:%d", *frontendHost, *frontendPort) frontendConn, err := net.Listen("tcp", frontendAddr) if err != nil { runtime.Error("ERROR: Cannot listen on %s: %v\n", frontendAddr, err) } var frontendListener net.Listener if *frontendTLS { certPath := path.Join(instanceDirectory, *certFile) keyPath := path.Join(instanceDirectory, *keyFile) tlsConfig := &tls.Config{ NextProtos: []string{"http/1.1"}, Rand: rand.Reader, Time: time.Seconds, } tlsConfig.Certificates = make([]tls.Certificate, 1) tlsConfig.Certificates[0], err = tls.LoadX509KeyPair(certPath, keyPath) if err != nil { runtime.Error("ERROR: Couldn't load certificate/key pair: %s\n", err) } frontendListener = tls.NewListener(frontendConn, tlsConfig) } else { frontendListener = frontendConn } var enforceHost bool var officialRedirectURL string var officialRedirectHTML []byte if len(*officialHost) != 0 { enforceHost = true if *frontendTLS { officialRedirectURL = "https://" + *officialHost + "/" } else { officialRedirectURL = "http://" + *officialHost + "/" } officialRedirectHTML = []byte(fmt.Sprintf(redirectHTML, officialRedirectURL)) } var frontendScheme, frontendAddrURL, httpAddrURL string if *frontendTLS { frontendScheme = "https://" } else { frontendScheme = "http://" } if len(*frontendHost) == 0 { frontendAddrURL = fmt.Sprintf("%slocalhost:%d", frontendScheme, *frontendPort) } else { frontendAddrURL = fmt.Sprintf("%s%s:%d", frontendScheme, *frontendHost, *frontendPort) } if len(*httpHost) == 0 { httpAddrURL = fmt.Sprintf("http://localhost:%d", *httpPort) } else { httpAddrURL = fmt.Sprintf("http://%s:%d", *httpHost, *httpPort) } var httpAddr string var httpListener net.Listener if !*noRedirect { if *redirectURL == "" { *redirectURL = frontendAddrURL } httpAddr = fmt.Sprintf("%s:%d", *httpHost, *httpPort) httpListener, err = net.Listen("tcp", httpAddr) if err != nil { runtime.Error("ERROR: Cannot listen on %s: %v\n", httpAddr, err) } } var rotate int switch *logRotate { case "daily": rotate = logging.RotateDaily case "hourly": rotate = logging.RotateHourly case "never": rotate = logging.RotateNever default: runtime.Error("ERROR: Unknown log rotation format %q\n", *logRotate) } if !*noConsoleLog { logging.AddConsoleLogger() logging.AddFilter(filterRequestLog) } _, err = logging.AddFileLogger("ampzero", logPath, rotate) if err != nil { runtime.Error("ERROR: Couldn't initialise logfile: %s\n", err) } fmt.Printf("Running ampzero with %d CPUs:\n", runtime.CPUCount) if !*noRedirect { redirector := &Redirector{url: *redirectURL} go func() { err = http.Serve(httpListener, redirector) if err != nil { runtime.Error("ERROR serving HTTP Redirector: %s\n", err) } }() fmt.Printf("* HTTP Redirector running on %s -> %s\n", httpAddrURL, *redirectURL) } frontend := &Frontend{ gaeAddr: gaeAddr, gaeHost: *gaeHost, gaeTLS: *gaeTLS, officialHost: *officialHost, officialRedirectURL: officialRedirectURL, officialRedirectHTML: officialRedirectHTML, enforceHost: enforceHost, } fmt.Printf("* Frontend Server running on %s\n", frontendAddrURL) err = http.Serve(frontendListener, frontend) if err != nil { runtime.Error("ERROR serving Frontend Server: %s\n", err) } }
func (op *OptionParser) Parse(args []string) (remainder []string) { if op.ParseHelp && !op.helpAdded { op.Bool([]string{"-h", "--help"}, false, "show this help and exit") op.helpAdded = true } if op.ParseVersion && !op.versionAdded { op.Bool([]string{"-v", "--version"}, false, "show the version and exit") op.versionAdded = true } argLength := len(args) - 1 // Command-line auto-completion support. autocomplete := os.Getenv("OPTPARSE_AUTO_COMPLETE") if autocomplete != "" { compWords := os.Getenv("COMP_WORDS") if compWords == "" { // zsh's bashcompinit does not pass COMP_WORDS, replace with // COMP_LINE for now... compWords = os.Getenv("COMP_LINE") if compWords == "" { runtime.Exit(1) } } compWordsList := strings.Split(compWords, " ", -1) compLine := os.Getenv("COMP_LINE") compPoint, err := strconv.Atoi(os.Getenv("COMP_POINT")) if err != nil { runtime.Exit(1) } compWord, err := strconv.Atoi(os.Getenv("COMP_CWORD")) if err != nil { runtime.Exit(1) } prefix := "" if compWord > 0 { if compWord < len(compWordsList) { prefix = compWordsList[compWord] } } // At some point in the future, make autocompletion customisable per // option flag and, at that point, make use of these variables. _ = compLine _ = compPoint // Pass to the shell completion if the previous word was a flag // expecting some parameter. if (compWord - 1) > 0 { prev := compWordsList[compWord-1] if strings.HasPrefix(prev, "--") { opt, ok := op.long2options[prev] if ok { if opt.dest != "" { runtime.Exit(1) } } } else if strings.HasPrefix(prev, "-") { opt, ok := op.short2options[prev] if ok { if opt.dest != "" { runtime.Exit(1) } } } } completions := make([]string, 0) for flag, _ := range op.long2options { if strings.HasPrefix(flag, prefix) { slice.AppendString(&completions, flag) } } for flag, _ := range op.short2options { if strings.HasPrefix(flag, prefix) { slice.AppendString(&completions, flag) } } fmt.Print(strings.Join(completions, " ")) runtime.Exit(1) } if argLength == 0 { return } var opt *option var ok bool idx := 1 for { arg := args[idx] noOpt := true if strings.HasPrefix(arg, "--") { opt, ok = op.long2options[arg] if ok { noOpt = false } } else if strings.HasPrefix(arg, "-") { opt, ok = op.short2options[arg] if ok { noOpt = false } } else { slice.AppendString(&remainder, arg) if idx == argLength { break } else { idx += 1 continue } } if noOpt { runtime.Error("%s: error: no such option: %s\n", args[0], arg) } if opt.dest != "" { if idx == argLength { runtime.Error("%s: error: %s option requires an argument\n", args[0], arg) } } if opt.valueType == "bool" { if opt.longflag == "--help" && op.ParseHelp { op.PrintUsage() runtime.Exit(1) } else if opt.longflag == "--version" && op.ParseVersion { fmt.Printf("%s\n", op.Version) runtime.Exit(0) } *opt.boolValue = true opt.defined = true idx += 1 } else if opt.valueType == "string" { if idx == argLength { runtime.Error("%s: error: no value specified for %s\n", args[0], arg) } *opt.stringValue = args[idx+1] opt.defined = true idx += 2 } else if opt.valueType == "int" { if idx == argLength { runtime.Error("%s: error: no value specified for %s\n", args[0], arg) } intValue, err := strconv.Atoi(args[idx+1]) if err != nil { runtime.Error("%s: error: couldn't convert %s value '%s' to an integer\n", args[0], arg, args[idx+1]) } *opt.intValue = intValue opt.defined = true idx += 2 } if idx > argLength { break } } for _, opt := range op.options { if opt.requiredFlag && !opt.defined { runtime.Error("%s: error: required: %s", args[0], opt) } } return }
func main() { // Define the options for the command line and config file options parser. opts := optparse.Parser( "Usage: wifistat <config.yaml> [options]\n", "wifistat 0.0.1") addr := opts.StringConfig("addr", ":9040", "the host:port address for the web server [:9040]") csv := opts.StringConfig("csv-dir", "csv", "the path to the csv files directory [csv]") wifi := opts.StringConfig("wifi-logs-dir", "iaslogs", "the path to the Wi-Fi logs directory [iaslogs]") membership := opts.BoolConfig("member-analytics", false, "enable membership-based analytics") devices := opts.StringConfig("devices-url", "", "the url key for the devices.csv Google Spreadsheet") members := opts.StringConfig("members-url", "", "the url key for the members.csv Google Spreadsheet") opening := opts.StringConfig("opening-url", "", "the url key for the opening.csv Google Spreadsheet") // Parse the command line options. os.Args[0] = "wifistat" _, root, _ := runtime.DefaultOpts("wifistat", opts, os.Args) // Compute option variables. wifiLogDir = runtime.JoinPath(root, *wifi) csvDir = runtime.JoinPath(root, *csv) err := os.MkdirAll(csvDir, 0755) if err != nil { runtime.StandardError(err) } // Handle member analytics options. if *membership { enableMemberAnalytics = true devicesUrlKey = *devices if devicesUrlKey == "" { runtime.Error("You need to specify the `devices-url` command-line option.") } membersUrlKey = *members if membersUrlKey == "" { runtime.Error("You need to specify the `members-url` command-line option.") } openingUrlKey = *opening if openingUrlKey == "" { runtime.Error("You need to specify the `opening-url` command-line option.") } } // Parse the logs. parseCsv(false) parseWifi() // Register the various handlers. http.HandleFunc("/", handleRequest) http.HandleFunc("/reload", handleReload) // Start the web server. log.Info("Running wifistat on %s", *addr) err = http.ListenAndServe(*addr, nil) if err != nil { runtime.StandardError(err) } runtime.Exit(0) }
func main() { // Setup temporary console logging. log.DisableConsoleTimestamp() log.AddConsoleLogger() // Set default values for command-line params. boltFilename := "Boltfile" genExecutablePath := "" recompile := false skipNext := true maxIdx := len(os.Args) - 1 newArgs := []string{"bolt"} // Extract higher-level command-line arguments. for idx, arg := range os.Args { if skipNext { skipNext = false continue } if arg == "--gen" && idx != maxIdx { var err error genExecutablePath, err = filepath.Abs(os.Args[idx+1]) if err != nil { runtime.StandardError(err) } skipNext = true } else if arg == "--boltfile" && idx != maxIdx { boltFilename = os.Args[idx+1] skipNext = true } else if arg == "--recompile" { recompile = true } else { newArgs = append(newArgs, arg) } } // Try and find the directory containing the Boltfile. boltdir, err := findBoltDir(boltFilename) if err != nil { if _, ok := err.(*fsutil.NotFound); ok { log.Error("Couldn't find Boltfile") runtime.Exit(1) } runtime.StandardError(err) } // Generate the path to the corresponding temp directory. boltpath := filepath.Join(boltdir, boltFilename) hash := sha1.New() hash.Write([]byte(boltpath)) digest := fmt.Sprintf("%x", hash.Sum(nil)) tempdir := filepath.Join(os.TempDir(), "bolt-"+digest) // See if the temp directory exists and if not create it. exists, err := fsutil.Exists(tempdir) if !exists { if _, ok := err.(*fsutil.NotFound); !ok { runtime.Error("Couldn't access the temp directory: %s: %s", tempdir, err) } err = os.Mkdir(tempdir, 0744) if err != nil { runtime.Error("Couldn't create the temp directory: %s: %s", tempdir, err) } } // See if an up-to-date generated binary already exists and, if so, run it. binpath := filepath.Join(tempdir, "bolt") if !recompile { boltstat, _ := os.Stat(boltpath) if genExecutablePath == "" { binstat, err := os.Stat(binpath) if err == nil { if boltstat.ModTime().Before(binstat.ModTime()) { runBoltExecutable(binpath, boltdir, newArgs) return } } } } // Parse the Boltfile. spec, err := parseBoltfile(boltpath, boltdir) if err != nil { exitForParserErrors(boltFilename, err) } // Exit if no tasks were found. if len(spec.Tasks) == 0 { runtime.Error("No tasks were found in %s", boltpath) } // Fudge the path to the executable that needs to be generated depending on // whether --gen-executable was specified or not. genOnly := true if genExecutablePath == "" { genExecutablePath = binpath genOnly = false } // Generate the executable. err = genExecutable(genExecutablePath, tempdir, spec) if err != nil { runtime.StandardError(err) } // Exit early if --gen-executable was specified. if genOnly { log.Info("%s successfully compiled to %s", boltFilename, genExecutablePath) runtime.Exit(0) } // Otherwise, run the executable. runBoltExecutable(binpath, boltdir, newArgs) }
func ampFrontend(argv []string, usage string) { // Define the options for the command line and config file options parser. opts := optparse.Parser( "Usage: amp frontend <config.yaml> [options]\n\n " + usage + "\n") httpsHost := opts.StringConfig("https-host", "", "the host to bind the HTTPS Frontends to") httpsPort := opts.IntConfig("https-port", 9040, "the base port for the HTTPS Frontends [9040]") officialHost := opts.StringConfig("offficial-host", "", "the official public host for the HTTPS Frontends") primaryHosts := opts.StringConfig("primary-hosts", "", "limit the primary HTTPS Frontend to the specified host pattern") primaryCert := opts.StringConfig("primary-cert", "cert/primary.cert", "the path to the primary host's TLS certificate [cert/primary.cert]") primaryKey := opts.StringConfig("primary-key", "cert/primary.key", "the path to the primary host's TLS key [cert/primary.key]") noSecondary := opts.BoolConfig("no-secondary", false, "disable the secondary HTTPS Frontend [false]") secondaryHosts := opts.StringConfig("secondary-hosts", "", "limit the secondary HTTPS Frontend to the specified host pattern") secondaryCert := opts.StringConfig("secondary-cert", "cert/secondary.cert", "the path to the secondary host's TLS certificate [cert/secondary.cert]") secondaryKey := opts.StringConfig("secondary-key", "cert/secondary.key", "the path to the secondary host's TLS key [cert/secondary.key]") errorDirectory := opts.StringConfig("error-dir", "error", "the path to the HTTP error files directory [error]") staticDirectory := opts.StringConfig("static-dir", "www", "the path to the static files directory [www]") staticMaxAge := opts.IntConfig("static-max-age", 86400, "max-age cache header value when serving the static files [86400]") hstsMaxAge := opts.IntConfig("hsts-max-age", 50000000, "max-age in seconds for HTTP Strict Transport Security [50000000]") noRedirect := opts.BoolConfig("no-redirect", false, "disable the HTTP Redirector [false]") httpHost := opts.StringConfig("http-host", "", "the host to bind the HTTP Redirector to") httpPort := opts.IntConfig("http-port", 9080, "the port to bind the HTTP Redirector to [9080]") httpRedirectURL := opts.StringConfig("redirect-url", "", "the URL that the HTTP Redirector redirects to") singleNode := opts.StringConfig("single-node", "", "the upstream single node address if running without a master") masterNodes := opts.StringConfig("master-nodes", "localhost:8060", "comma-separated addresses of amp master nodes [localhost:8060]") masterCert := opts.StringConfig("master-cert", "cert/master.cert", "the path to the amp master certificate [cert/master.cert]") ironKeyPath := opts.StringConfig("iron-key", "cert/iron.key", "the path to the key used for iron strings [cert/iron.key]") maintenanceMode := opts.BoolConfig("maintenance", false, "start up in maintenance mode [false]") _, instanceDirectory, _ := runtime.DefaultOpts("frontend", opts, argv) // Ensure that the directory containing static files exists. staticPath := runtime.JoinPath(instanceDirectory, *staticDirectory) dirInfo, err := os.Stat(staticPath) if err == nil { if !dirInfo.IsDirectory() { runtime.Error("%q is not a directory", staticPath) } } else { runtime.StandardError(err) } // Ensure that the directory containing error files exists. errorPath := runtime.JoinPath(instanceDirectory, *errorDirectory) dirInfo, err = os.Stat(errorPath) if err == nil { if !dirInfo.IsDirectory() { runtime.Error("%q is not a directory", errorPath) } } else { runtime.StandardError(err) } // If ``--official-host`` hasn't been specified, generate it from the given // frontend host and base port values -- assuming ``localhost`` for a blank // host. publicHost := *officialHost if publicHost == "" { if *httpsHost == "" { publicHost = fmt.Sprintf("localhost:%d", *httpsPort) } else { publicHost = fmt.Sprintf("%s:%d", *httpsHost, *httpsPort) } } // Compute the HSTS max age header value. hsts := fmt.Sprintf("max-age=%d", *hstsMaxAge) // Pre-format the Cache-Control header for static files. staticCache := fmt.Sprintf("public, max-age=%d", *staticMaxAge) staticMaxAge64 := int64(*staticMaxAge) // Compute the variables related to redirects. redirectURL := "https://" + publicHost redirectHTML := []byte(fmt.Sprintf( `Please <a href="%s">click here if your browser doesn't redirect</a> automatically.`, redirectURL)) // Compute the path to the Iron key. ironPath := runtime.JoinPath(instanceDirectory, *ironKeyPath) // Instantiate the master client. masterClient, err := master.NewClient( *masterNodes, runtime.JoinPath(instanceDirectory, *masterCert)) if err != nil { runtime.StandardError(err) } var noMaster bool if *singleNode != "" { noMaster = true } // Let the user know how many CPUs we're currently running on. log.Info("Running the Amp Frontend on %d CPUs.", runtime.CPUCount) // Initialise the TLS config. tlsconf.Init() // Initialise a container for the HTTPSFrontends. webFrontends := make([]*server.HTTPSFrontend, 1) // Compute the variables related to detecting valid hosts. primaryWildcard, primaryAddr := getValidAddr(*primaryHosts) secondaryWildcard, secondaryAddr := getValidAddr(*secondaryHosts) // Instantiate the primary ``HTTPSFrontend`` object. frontend := &server.HTTPSFrontend{ HSTS: hsts, MaintenanceMode: *maintenanceMode, MasterClient: masterClient, NoMaster: noMaster, RedirectHTML: redirectHTML, RedirectURL: redirectURL, SingleNode: *singleNode, StaticCache: staticCache, StaticMaxAge: staticMaxAge64, ValidAddress: primaryAddr, ValidWildcard: primaryWildcard, } frontend.LoadAssets(errorPath, ironPath, staticPath) frontend.Run(*httpsHost, *httpsPort, runtime.JoinPath(instanceDirectory, *primaryCert), runtime.JoinPath(instanceDirectory, *primaryKey)) webFrontends[0] = frontend // Setup and run the secondary HTTPSFrontend. if !*noSecondary { frontend = &server.HTTPSFrontend{ HSTS: hsts, MaintenanceMode: *maintenanceMode, MasterClient: masterClient, NoMaster: noMaster, RedirectHTML: redirectHTML, RedirectURL: redirectURL, SingleNode: *singleNode, StaticCache: staticCache, StaticMaxAge: staticMaxAge64, ValidAddress: secondaryAddr, ValidWildcard: secondaryWildcard, } frontend.LoadAssets(errorPath, ironPath, staticPath) frontend.Run(*httpsHost, *httpsPort+1, runtime.JoinPath(instanceDirectory, *secondaryCert), runtime.JoinPath(instanceDirectory, *secondaryKey)) webFrontends = append(webFrontends, frontend) } // Create a channel which is used to toggle maintenance mode based on // process signals. maintenanceChannel := make(chan bool, 1) // Fork a goroutine which toggles the maintenance mode in a single place and // thus ensures thread safety. go func() { for { enabledState := <-maintenanceChannel for _, frontend := range webFrontends { if enabledState { frontend.MaintenanceMode = true } else { frontend.LoadAssets(errorPath, ironPath, staticPath) frontend.MaintenanceMode = false } } } }() // Register the signal handlers for SIGUSR1 and SIGUSR2. runtime.SignalHandlers[os.SIGUSR1] = func() { maintenanceChannel <- true } runtime.SignalHandlers[os.SIGUSR2] = func() { maintenanceChannel <- false } // Enter a wait loop if the HTTP Redirector has been disabled. if *noRedirect { loopForever := make(chan bool, 1) <-loopForever } // Otherwise, setup and run the HTTP Redirector. if *httpHost == "" { *httpHost = "localhost" } if *httpRedirectURL == "" { *httpRedirectURL = "https://" + publicHost } redirector := &server.HTTPRedirector{*httpRedirectURL} redirector.Run(*httpHost, *httpPort) // Enter the wait loop for the process to be killed. loopForever := make(chan bool, 1) <-loopForever }