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 } }
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 }
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 } } } }
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")) } } }
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 }
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 }
// 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 }