func (p *Process) startCommand() { // p.Stdout.Reset() // p.Stderr.Reset() // p.Output.Reset() // Donot reset because log is still needed. log.Printf("start cmd(%s): %s", p.Name, p.Command) p.cmd = p.buildCommand() p.SetState(Running) if err := p.cmd.Start(); err != nil { log.Warnf("program %s start failed: %v", p.Name, err) p.SetState(Fatal) return } go func() { errC := GoFunc(p.cmd.Wait) startTime := time.Now() select { case <-errC: // if p.cmd.Wait() returns, it means program and its sub process all quited. no need to kill again // func Wait() will only return when program session finishs. (Only Tested on mac) log.Printf("program(%s) finished, time used %v", p.Name, time.Since(startTime)) if time.Since(startTime) < time.Duration(p.StartSeconds)*time.Second { if p.retryLeft == p.StartRetries { // If first time quit so fast, just set to fatal p.SetState(Fatal) log.Printf("program(%s) exit too quick, status -> fatal", p.Name) return } } p.waitNextRetry() case <-p.stopC: log.Println("recv stop command") p.stopCommand() // clean up all process } }() }
func Register(dict *Dict, nsprefix string, self interface{}) (ctx RegisterCtx) { typ := reflect.TypeOf(self) selfv := reflect.ValueOf(self) nmethod := typ.NumMethod() for i := 0; i < nmethod; i++ { method := typ.Method(i) mtype := method.Type mname := method.Name if mtype.PkgPath() != "" || !strings.HasPrefix(mname, "Py_") { continue } nin := mtype.NumIn() name := mname[3:] fullname := nsprefix + name if nin == 3 && sigMatches(mtype, typTernaryCallFunc) || nin == 2 && sigMatches(mtype, typBinaryCallFunc) { closure := &Closure{selfv, method.Func} f := closure.NewFunction(fullname, nin, "") dict.SetItemString(name, f) f.Decref() ctx = append(ctx, closure) log.Debug("Register", fullname) } else { log.Warnf("Invalid signature of method %s, register failed", fullname) continue } } return }
func Open(file string) (f *os.File, err error) { if f, err = os.Open(file); err != nil { return } oldFname, pos, err := fgetPos(f) if err != nil { if err == ErrNotFound { err = nil } else { f.Close() f = nil } return f, err } info, err := f.Stat() if err != nil { f.Close() f = nil return } if info.Size() == pos { f.Close() return nil, ErrNoUpdate } else if info.Size() < pos { nowFname := filepath.Base(file) if nowFname == oldFname { log.Warnf("Position found truncate file, %s(%d => %d)", file, pos, info.Size()) pos = info.Size() } else { // inode same & fname not same & truncated, consider it is a new file log.Warnf("Position found same inode file %s %s", file, oldFname) pos = 0 } } if _, err = f.Seek(pos, os.SEEK_SET); err != nil { f.Close() f = nil } return }
func (s senders) Send(events []Event) error { var err error for i := 0; i < s.retryTimes; i++ { if err = s.send(events); err == nil { break } log.Warnf("Sender remain retryTimes(%d) failed - %v", s.retryTimes-i-1, err) } return err }
func idleWsSend(wsc *websocket.Conn) { var msg Msg msg.Type = TYPE_IDLE msg.Name = "idle" for { if err := wsc.WriteJSON(&msg); err != nil { log.Warnf("write idle msg error: %v", err) break } time.Sleep(5 * time.Second) } }
// FIXME(ssx): maybe need to return error func (p *Process) buildCommand() *kexec.KCommand { cmd := kexec.CommandString(p.Command) // cmd := kexec.Command(p.Command[0], p.Command[1:]...) logDir := filepath.Join(defaultConfigDir, "log", sanitize.Name(p.Name)) if !IsDir(logDir) { os.MkdirAll(logDir, 0755) } var fout io.Writer var err error p.OutputFile, err = os.OpenFile(filepath.Join(logDir, "output.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { log.Warn("create stdout log failed:", err) fout = ioutil.Discard } else { fout = p.OutputFile } cmd.Stdout = io.MultiWriter(p.Stdout, p.Output, fout) cmd.Stderr = io.MultiWriter(p.Stderr, p.Output, fout) // config environ cmd.Env = os.Environ() // inherit current vars environ := map[string]string{} if p.User != "" { err := cmd.SetUser(p.User) if err != nil { log.Warnf("[%s] chusr to %s failed", p.Name, p.User) } else { var homeDir string switch runtime.GOOS { case "linux": homeDir = "/home/" + p.User // FIXME(ssx): maybe there is a better way case "darwin": homeDir = "/Users/" + p.User } cmd.Env = append(cmd.Env, "HOME="+homeDir, "USER="******"HOME"] = homeDir environ["USER"] = p.User } } cmd.Env = append(cmd.Env, p.Environ...) mapping := func(key string) string { val := os.Getenv(key) if val != "" { return val } return environ[key] } cmd.Dir = os.Expand(p.Dir, mapping) if strings.HasPrefix(cmd.Dir, "~") { cmd.Dir = mapping("HOME") + cmd.Dir[1:] } log.Infof("[%s] use dir: %s\n", p.Name, cmd.Dir) return cmd }
func (p *Program) Run() (err error) { if err = p.Start(); err != nil { return } p.setStatus(ST_RUNNING) defer func() { if out, ok := p.Cmd.Stdout.(io.Closer); ok { out.Close() } log.Warnf("program finish: %v", err) }() err = p.Wait() return }
func (s *Supervisor) hWebhook(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, category := vars["name"], vars["category"] proc, ok := s.procMap[name] if !ok { http.Error(w, fmt.Sprintf("proc %s not exist", strconv.Quote(name)), http.StatusForbidden) return } hook := proc.Program.WebHook if category == "github" { gh := hook.Github _ = gh.Secret isRunning := proc.IsRunning() s.stopAndWait(name) go func() { cmd := kexec.CommandString(hook.Command) cmd.Dir = proc.Program.Dir cmd.Stdout = proc.Output cmd.Stderr = proc.Output err := GoTimeout(cmd.Run, time.Duration(hook.Timeout)*time.Second) if err == ErrGoTimeout { cmd.Terminate(syscall.SIGTERM) } if err != nil { log.Warnf("webhook command error: %v", err) // Trigger pushover notification } if isRunning { proc.Operate(StartEvent) } }() io.WriteString(w, "success triggered") } else { log.Warnf("unknown webhook category: %v", category) } }
func (p *Program) RunNotification() { po := p.Notifications.Pushover if po.ApiKey != "" && len(po.Users) > 0 { for _, user := range po.Users { err := pushover.Notify(pushover.Params{ Token: po.ApiKey, User: user, Title: "gosuv", Message: fmt.Sprintf("%s change to fatal", p.Name), }) if err != nil { log.Warnf("pushover error: %v", err) } } } }
func proxyHandler(w http.ResponseWriter, r *http.Request) { var proxyFor = r.Header.Get("X-Proxy-For") log.Infof("[remote %s] X-Proxy-For [%s]", r.RemoteAddr, proxyFor) connC, ok := namedConnection[proxyFor] if !ok { http.Error(w, "inside error: proxy not ready to receive conn", http.StatusInternalServerError) return } conn, err := hijackHTTPRequest(w) if err != nil { log.Warnf("hijeck failed, %v", err) connC <- nil return } connC <- conn }
func handleWsMsg(msg Msg, sURL *url.URL, rnl *RevNetListener) { u := sURL switch msg.Type { case TYPE_NEWCONN: log.Debug("dial remote:", u.Host) sconn, err := net.Dial("tcp", u.Host) if err != nil { log.Println(err) break } _, err = sconn.Write([]byte(fmt.Sprintf( "GET /proxyhijack HTTP/1.1\r\nX-Proxy-For: %s \r\n\r\n", msg.Name))) if err != nil { log.Println(err) break } rnl.connCh <- sconn case TYPE_MESSAGE: fmt.Printf("Recv Message: %v\n", msg.Body) default: log.Warnf("Type: %v not support", msg.Type) } }
func (w *WriteBroadcaster) Write(p []byte) (n int, err error) { w.Lock() defer w.Unlock() // write with advance w.buf.WriteAndMaybeOverwriteOldestData(p) for sw := range w.writers { // set write timeout err = GoTimeout(func() error { if _, err := sw.wc.Write(p); err != nil { //|| n != len(p) { return errors.New("broadcast to " + sw.stream + " error") } return nil }, time.Second*1) if err != nil { // On error, evict the writer log.Warnf("broadcase write error: %s, %s", sw.stream, err) sw.wc.Close() delete(w.writers, sw) } } return len(p), nil }
func (ps *ProxyServer) newControlHandler() func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { // read listen port from request protocol, subdomain, port := parseConnectRequest(r) log.Debugf("proxy listen proto: %v, subdomain: %v port: %v", protocol, subdomain, port) // create websocket connection conn, err := upgrader.Upgrade(w, r, nil) if err != nil { http.Error(w, err.Error(), 502) return } defer conn.Close() log.Debug("remote client addr:", conn.RemoteAddr()) tunnel := &Tunnel{ wsconn: conn, } // TCP: create new port to listen log.Infof("New %s proxy for %v", protocol, conn.RemoteAddr()) switch protocol { case "tcp": // proxyAddr := fmt.Sprintf("0.0.0.0:%d", port) listener, err := NewTcpProxyListener(tunnel, port) if err != nil { log.Warnf("new tcp proxy err: %v", err) http.Error(w, err.Error(), 501) return } defer listener.Close() _, port, _ := net.SplitHostPort(listener.Addr().String()) wsSendMessage(conn, fmt.Sprintf( "Local tcp conn is now publicly available via:\n%v:%v\n", ps.domain, port)) case "http", "https": tr := &http.Transport{ Dial: tunnel.generateTransportDial(), } revProxy := &httputil.ReverseProxy{ Director: func(req *http.Request) { log.Println("director:", req.RequestURI) }, Transport: tr, } // should hook here // hook(HOOK_CREATE_HTTP_SUBDOMAIN, subdomain) // generate a uniq domain if subdomain == "" { subdomain = uniqName(5) + ".t" } pxDomain := subdomain + "." + ps.domain log.Println("http px use domain:", pxDomain) if _, exists := ps.revProxies[pxDomain]; exists { wsSendMessage(conn, fmt.Sprintf("subdomain [%s] has already been taken", pxDomain)) return } ps.Lock() ps.revProxies[pxDomain] = revProxy ps.Unlock() wsSendMessage(conn, fmt.Sprintf( "Local server is now publicly available via:\nhttp://%s\n", pxDomain)) defer func() { ps.Lock() delete(ps.revProxies, pxDomain) ps.Unlock() }() default: log.Warn("unknown protocol:", protocol) return } // HTTP: use httputil.ReverseProxy for { var msg Msg if err := conn.ReadJSON(&msg); err != nil { log.Warn(err) break } log.Info("recv json:", msg) } } }