Пример #1
0
func (s *Service) listen(addr skynet.BindAddr, bindWait *sync.WaitGroup) {
	var err error
	s.rpcListener, err = addr.Listen()
	if err != nil {
		panic(err)
	}

	log.Printf(log.INFO, "%+v\n", ServiceListening{
		Addr:        &addr,
		ServiceInfo: s.ServiceInfo,
	})

	// We may have changed port due to conflict, ensure config has the correct port now
	a, _ := skynet.BindAddrFromString(addr.String())
	s.ServiceAddr.IPAddress = a.IPAddress
	s.ServiceAddr.Port = a.Port

	bindWait.Done()

	for {
		conn, err := s.rpcListener.AcceptTCP()

		if s.shuttingDown {
			break
		}

		if err != nil && !s.shuttingDown {
			log.Println(log.ERROR, "AcceptTCP failed", err)
			continue
		}
		s.connectionChan <- conn
	}
}
Пример #2
0
func (s *Service) unregister() {
	// this version must be run from the mux() goroutine
	if !s.Registered {
		return
	}

	err := skynet.GetServiceManager().Unregister(s.UUID)
	if err != nil {
		log.Println(log.ERROR, "Failed to unregister service: "+err.Error())
	}

	s.Registered = false
	log.Printf(log.INFO, "%+v\n", ServiceUnregistered{s.ServiceInfo})
	s.Delegate.Unregistered(s) // Call user defined callback
}
Пример #3
0
func watchSignals(c chan os.Signal, s *Service) {
	signal.Notify(c, syscall.SIGINT, syscall.SIGKILL, syscall.SIGSEGV, syscall.SIGSTOP, syscall.SIGTERM)

	for {
		select {
		case sig := <-c:
			switch sig.(syscall.Signal) {
			// Trap signals for clean shutdown
			case syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT,
				syscall.SIGSEGV, syscall.SIGSTOP, syscall.SIGTERM:
				log.Printf(log.INFO, "%+v", KillSignal{sig.(syscall.Signal)})
				s.Shutdown()
				return
			}
		}
	}
}
Пример #4
0
func (s *Service) serveAdminRequests() {
	rId := os.Stderr.Fd() + 2
	wId := os.Stderr.Fd() + 3

	pipeReader := os.NewFile(uintptr(rId), "")
	pipeWriter := os.NewFile(uintptr(wId), "")
	s.pipe = daemon.NewPipe(pipeReader, pipeWriter)

	b := make([]byte, daemon.MAX_PIPE_BYTES)
	for {
		n, err := s.pipe.Read(b)

		if err != nil {
			if err != io.EOF {
				log.Printf(log.ERROR, "Error reading from admin pipe "+err.Error())
			} else {
				// We received EOF, ensure we shutdown (if daemon died we could be orphaned)
				s.Shutdown()
			}

			return
		}

		cmd := string(b[:n])
		log.Println(log.TRACE, "Received "+cmd+" from daemon")

		switch cmd {
		case "SHUTDOWN":
			s.Shutdown()
			s.pipe.Write([]byte("ACK"))
			break
		case "REGISTER":
			s.Register()
			s.pipe.Write([]byte("ACK"))
		case "UNREGISTER":
			s.Unregister()
			s.pipe.Write([]byte("ACK"))
		case "LOG DEBUG", "LOG TRACE", "LOG INFO", "LOG WARN", "LOG ERROR", "LOG FATAL", "LOG PANIC":
			parts := strings.Split(cmd, " ")
			log.SetLogLevel(log.LevelFromString(parts[1]))
			log.Println(log.INFO, "Setting log level to "+parts[1])

			s.pipe.Write([]byte("ACK"))
		}
	}
}
Пример #5
0
func (s *SkynetDaemon) StartSubService(requestInfo *skynet.RequestInfo, in daemon.StartSubServiceRequest, out *daemon.StartSubServiceResponse) (err error) {
	out.UUID = config.NewUUID()

	log.Printf(log.TRACE, "%+v", SubserviceStart{
		BinaryName: in.BinaryName,
		Args:       in.Args,
	})

	ss, err := NewSubService(in.BinaryName, in.Args, out.UUID, in.Registered)
	if err != nil {
		return
	}

	s.serviceLock.Lock()
	s.Services[out.UUID] = ss
	s.serviceLock.Unlock()

	start, startErr := ss.Start()

	if startErr != nil {
		return errors.New("Service failed to start: " + startErr.Error())
	} else if !start {
		return errors.New("Service failed to start")
	}

	tc := time.Tick(RerunWait * 2)

	go func() {
		// Wait for startup timer to see if we're still running
		// We want to avoid keeping a state of a large list of services that failed to start
		<-tc

		if ss.IsRunning() {
			s.saveState()
		}
	}()

	return
}
Пример #6
0
func NewServiceRPC(s *Service) (srpc *ServiceRPC) {
	srpc = &ServiceRPC{
		service: s,
		methods: make(map[string]reflect.Value),
	}

	// scan through methods looking for a method (RequestInfo,
	// something, something) error
	typ := reflect.TypeOf(srpc.service.Delegate)
	for i := 0; i < typ.NumMethod(); i++ {
		m := typ.Method(i)

		if reservedMethodNames[m.Name] {
			continue
		}

		// this is the check to see if something is exported
		if m.PkgPath != "" {
			continue
		}

		f := m.Func
		ftyp := f.Type()

		// must have four parameters: (receiver, RequestInfo,
		// somethingIn, somethingOut)
		if ftyp.NumIn() != 4 {
			goto problem
		}

		// don't have to check for the receiver

		// check the second parameter
		if ftyp.In(1) != RequestInfoPtrType {
			goto problem
		}

		// the somethingIn can be anything

		// somethingOut must be a pointer or a map
		switch ftyp.In(3).Kind() {
		case reflect.Ptr, reflect.Map:
		default:
			goto problem
		}

		// must have one return value that is an error
		if ftyp.NumOut() != 1 {
			goto problem
		}
		if ftyp.Out(0) != ErrorType {
			goto problem
		}

		// we've got a method!
		srpc.methods[m.Name] = f
		srpc.MethodNames = append(srpc.MethodNames, m.Name)
		continue

	problem:
		log.Printf(log.WARN, "Bad RPC method for %T: %q %v\n", s.Delegate, m.Name, f)
	}

	return
}
Пример #7
0
// ServiceRPC.Forward is the entry point for RPC calls. It wraps actual RPC calls
// and provides a slot for the RequestInfo. The parameters to the actual RPC
// calls are transmitted in a []byte, and are then marshalled/unmarshalled on
// either end.
func (srpc *ServiceRPC) Forward(in skynet.ServiceRPCInRead, out *skynet.ServiceRPCOutWrite) (err error) {
	srpc.service.activeRequests.Add(1)
	defer srpc.service.activeRequests.Done()

	go stats.MethodCalled(in.Method)

	clientInfo, ok := srpc.service.getClientInfo(in.ClientID)
	if !ok {
		err = errors.New("did not provide the ClientID")
		log.Printf(log.ERROR, "%+v", MethodError{in.RequestInfo, in.Method, err})
		return
	}

	in.RequestInfo.ConnectionAddress = clientInfo.Address.String()
	if in.RequestInfo.OriginAddress == "" || !srpc.service.IsTrusted(clientInfo.Address) {
		in.RequestInfo.OriginAddress = in.RequestInfo.ConnectionAddress
	}

	mc := MethodCall{
		MethodName:  in.Method,
		RequestInfo: in.RequestInfo,
	}

	log.Printf(log.INFO, "%+v", mc)

	m, ok := srpc.methods[in.Method]
	if !ok {
		err = errors.New(fmt.Sprintf("No such method %q", in.Method))
		log.Printf(log.ERROR, "%+v", MethodError{in.RequestInfo, in.Method, err})
		return
	}

	inValuePtr := reflect.New(m.Type().In(2))

	err = bson.Unmarshal(in.In, inValuePtr.Interface())
	if err != nil {
		log.Println(log.ERROR, "Error unmarshaling request ", err)
		return
	}

	// Allocate the out parameter of the RPC call.
	outType := m.Type().In(3)
	var outValue reflect.Value

	switch outType.Kind() {
	case reflect.Ptr:
		outValue = reflect.New(m.Type().In(3).Elem())
	case reflect.Map:
		outValue = reflect.MakeMap(outType)
	default:
		err = errors.New("illegal out param type")
		log.Printf(log.ERROR, "%+v", MethodError{in.RequestInfo, in.Method, err})
		return
	}

	startTime := time.Now()

	params := []reflect.Value{
		reflect.ValueOf(srpc.service.Delegate),
		reflect.ValueOf(in.RequestInfo),
		inValuePtr.Elem(),
		outValue,
	}

	returns := m.Call(params)

	duration := time.Now().Sub(startTime)

	mcp := MethodCompletion{
		MethodName:  in.Method,
		RequestInfo: in.RequestInfo,
		Duration:    duration,
	}

	log.Printf(log.INFO, "%+v", mcp)

	var b []byte
	b, err = bson.Marshal(outValue.Interface())
	if err != nil {
		log.Printf(log.ERROR, "%+v", MethodError{in.RequestInfo, in.Method, fmt.Errorf("Error marshaling response", err)})
		return
	}

	out.Out = bson.Binary{
		0x00,
		b,
	}

	var rerr error = nil
	erri := returns[0].Interface()
	if erri != nil {
		rerr, _ = erri.(error)
		out.ErrString = rerr.Error()

		log.Printf(log.ERROR, "%+v", MethodError{in.RequestInfo, in.Method, fmt.Errorf("Method returned error:", err)})
	}

	go stats.MethodCompleted(in.Method, duration, rerr)

	return
}