Пример #1
0
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
		}
	}()
}
Пример #2
0
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
}
Пример #3
0
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
}
Пример #4
0
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
}
Пример #5
0
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)
	}
}
Пример #6
0
// 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
}
Пример #7
0
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
}
Пример #8
0
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)
	}
}
Пример #9
0
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)
			}
		}
	}
}
Пример #10
0
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
}
Пример #11
0
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)
	}
}
Пример #12
0
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
}
Пример #13
0
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)
		}
	}
}