func UnixAudienceListener(sockaddr string) { fi, err := os.Stat(sockaddr) if err == nil { fmode := fi.Mode() if fmode&os.ModeType == os.ModeSocket { o.Warn("Removing stale socket at %s", sockaddr) os.Remove(sockaddr) } else { o.Fail("%s exists and is not a socket", sockaddr) } } laddr, err := net.ResolveUnixAddr("unix", sockaddr) o.MightFail(err, "Couldn't resolve audience socket address") l, err := net.ListenUnix("unix", laddr) o.MightFail(err, "Couldn't start audience unixsock listener") // Fudge the permissions on the unixsock! fi, err = os.Stat(sockaddr) if err == nil { os.Chmod(sockaddr, fi.Mode()|0777) } else { o.Warn("Couldn't fudge permission on audience socket: %s", err) } // make sure we clean up the unix socket when we die. defer l.Close() defer os.Remove(sockaddr) AudienceListener(l) }
func loadLastId() { fh, err := os.Open(checkpointPath()) if err == nil { defer fh.Close() // we have a checkpoint file. blah. cbio := bufio.NewReader(fh) l, err := cbio.ReadString('\n') lastId, err = strconv.ParseUint(strings.TrimSpace(l), 10, 64) if err != nil { o.Fail("Couldn't read last ID from checkpoint file") } lastId += IdCheckpointSafetySkip } else { if !os.IsNotExist(err) { o.Fail("Found checkpoint file, but couldn't open it: %s", err) } fh, err := os.Open(savePath()) if err != nil { if os.IsNotExist(err) { lastId = 0 return } o.MightFail(err, "Couldn't open last_id file") } defer fh.Close() cbio := bufio.NewReader(fh) l, err := cbio.ReadString('\n') lastId, err = strconv.ParseUint(strings.TrimSpace(l), 10, 64) if err != nil { o.Fail("Couldn't read last ID from last_id") } } writeIdCheckpoint() }
func UnixAudienceListener(sockaddr string, sockmode os.FileMode, sockuid int, sockgid int) { fi, err := os.Stat(sockaddr) if err == nil { if (fi.Mode() & os.ModeSocket) != 0 { os.Remove(sockaddr) } else { o.Fail("%s exists and is not a socket", sockaddr) } } err = os.MkdirAll(path.Dir(sockaddr), 0755) o.MightFail(err, "Couldn't create socket directory") laddr, err := net.ResolveUnixAddr("unix", sockaddr) o.MightFail(err, "Couldn't resolve audience socket address") old_umask := syscall.Umask(0777) defer syscall.Umask(old_umask) l, err := net.ListenUnix("unix", laddr) o.MightFail(err, "Couldn't start audience unixsock listener") if sockuid >= 0 || sockgid >= 0 { err = os.Chown(sockaddr, sockuid, sockgid) o.MightFail(err, "Couldn't chown audience unixsock listener") } err = os.Chmod(sockaddr, sockmode) o.MightFail(err, "Couldn't chmod audience unixsock listener") // make sure we clean up the unix socket when we die. defer l.Close() defer os.Remove(sockaddr) AudienceListener(l) }
func ConfigLoad() { // attempt to open the configuration file. fh, err := os.Open(*ConfigFile) if nil == err { defer fh.Close() // reset the config File data, then reload it. configFile.Reset() ierr := configFile.Read(fh, 1) o.MightFail(ierr, "Couldn't parse configuration") } else { o.Warn("Couldn't open configuration file: %s. Proceeding anyway.", err) } playerpath := strings.TrimSpace(GetStringOpt("player file path")) pfh, err := os.Open(playerpath) o.MightFail(err, "Couldn't open \"%s\"", playerpath) pbr := bufio.NewReader(pfh) ahmap := make(map[string]bool) for err = nil; err == nil; { var lb []byte var prefix bool lb, prefix, err = pbr.ReadLine() if nil == lb { break } if prefix { o.Fail("ConfigLoad: Short Read (prefix only)!") } line := strings.TrimSpace(string(lb)) if line == "" { continue } if line[0] == '#' { continue } ahmap[line] = true } // convert newAuthorisedHosts to a slice authorisedHosts := make([]string, len(ahmap)) idx := 0 for k, _ := range ahmap { authorisedHosts[idx] = k idx++ } ClientUpdateKnown(authorisedHosts) // set the spool directory SetSpoolDirectory(GetStringOpt("conductor state path")) }
func ProcessingLoop() { var conn net.Conn = nil var nextRetryResp *TaskResponse = nil var taskCompletionChan <-chan *TaskResponse = nil var connectDelay time.Duration var doScoreReload bool = false // kick off a new connection attempt. go connectMe(connectDelay) // and this is where we spin! for { var retryDelay time.Duration = 0 var retryChan <-chan time.Time = nil if conn != nil { for nextRetryResp == nil { nextRetryResp = getNextUnacknowledgedResponse() if nil == nextRetryResp { break } retryDelay = nextRetryResp.RetryTime.Sub(time.Now()) if retryDelay < 0 { sendResponse(conn, nextRetryResp) nextRetryResp = nil } } if nextRetryResp != nil { retryChan = time.After(retryDelay) } } if taskCompletionChan == nil { nextTask := getNextPendingTask() if nextTask != nil { taskCompletionChan = ExecuteTask(nextTask) } else { if conn != nil && !pendingTaskRequest { o.Debug("Asking for trouble") p := o.MakeReadyForTask() p.Send(conn) o.Debug("Sent Request for trouble") pendingTaskRequest = true } } } select { // Currently executing job finishes. case newresp := <-taskCompletionChan: o.Debug("job%d: Completed with State %s\n", newresp.id, newresp.State) // preemptively set a retrytime. newresp.RetryTime = time.Now() // ENOCONN - sub it in as our next retryresponse, and prepend the old one onto the queue. if nil == conn { if nil != nextRetryResp { prequeueResponse(nextRetryResp) } o.Debug("job%d: Queuing Initial Response", newresp.id) nextRetryResp = newresp } else { o.Debug("job%d: Sending Initial Response", newresp.id) sendResponse(conn, newresp) } if doScoreReload { o.Info("Performing Deferred score reload") LoadScores() doScoreReload = false } taskCompletionChan = nil // If the current unacknowledged response needs a retry, send it. case <-retryChan: sendResponse(conn, nextRetryResp) nextRetryResp = nil // New connection. Set up the receiver thread and Introduce ourselves. case nci := <-newConnection: if conn != nil { conn.Close() } conn = nci.conn connectDelay = nci.timeout pendingTaskRequest = false // start the reader go Reader(conn) /* Introduce ourself */ p := o.MakeIdentifyClient(LocalHostname, PlayerVersion) p.Send(conn) // Lost connection. Shut downt he connection. case <-lostConnection: o.Warn("Lost Connection to Master") conn.Close() conn = nil // restart the connection attempts go connectMe(connectDelay) // Message received from master. Decode and action. case p := <-receivedMessage: // because the message could possibly be an ACK, push the next retry response back into the queue so acknowledge can find it. if nil != nextRetryResp { prequeueResponse(nextRetryResp) nextRetryResp = nil } var upkt interface{} = nil if p.Length > 0 { var err error upkt, err = p.Decode() o.MightFail(err, "Couldn't decode packet from master") } handler, exists := dispatcher[p.Type] if exists { connectDelay = 0 handler(conn, upkt) } else { o.Fail("Unhandled Pkt Type %d", p.Type) } // Reload scores case <-reloadScores: // fortunately this is actually completely safe as // long as nobody's currently executing. // who'd have thunk it? if taskCompletionChan == nil { o.Info("Reloading scores") LoadScores() } else { o.Info("Deferring score reload (execution in progress)") doScoreReload = true } // Keepalive delay expired. Send Nop. case <-time.After(KeepaliveDelay): if conn == nil { break } o.Debug("Sending NOP") p := o.MakeNop() p.Send(conn) } } }
func handleIllegal(c net.Conn, message interface{}) { o.Fail("Got Illegal Message") }
/* C->P only messages, should never appear on the wire. */ o.TypeTaskRequest: handleIllegal, } var loopFudge time.Duration = 10 * time.Millisecond /* 10 ms should be enough fudgefactor */ func clientLogic(client *ClientInfo) { loop := true for loop { var retryWait <-chan time.Time var retryTask *TaskRequest if client.Player != "" { var waitTime, now time.Time cleanPass := false attempts := 0 for !cleanPass && attempts < 10 { /* reset our state for the pass */ waitTime = time.Time{} retryTask = nil attempts++ cleanPass = true now = time.Now().Add(loopFudge) // if the client is correctly associated, // evaluate all jobs for outstanding retries, // and work out when our next retry is due. for _, v := range client.pendingTasks { if v.RetryTime.Before(now) { client.SendTask(v) cleanPass = false } else { if waitTime == (time.Time{}) || v.RetryTime.Before(waitTime) { retryTask = v waitTime = v.RetryTime } } } } if attempts > 10 { o.Fail("Couldn't find next timeout without restarting excessively") } if retryTask != nil { retryWait = time.After(waitTime.Sub(time.Now())) } } select { case <-retryWait: client.SendTask(retryTask) case p := <-client.PktInQ: /* we've received a packet. do something with it. */ if client.Player == "" && p.Type != o.TypeIdentifyClient { o.Warn("Client %s: didn't identify self,- got type %d instead", client.Name(), p.Type) client.Abort() break } var upkt interface{} if p.Length > 0 { var err error upkt, err = p.Decode() if err != nil { o.Warn("Client %s: error unmarshalling message: %s", client.Name(), err) client.Abort() break } } handler, exists := dispatcher[p.Type] if exists { handler(client, upkt) } else { o.Warn("Client %s: Unhandled packet type: %d", client.Name(), p.Type) } case p := <-client.PktOutQ: if p != nil { client.sendNow(p) } case t := <-client.TaskQ: client.GotTask(t) case <-client.abortQ: loop = false case <-time.After(KeepaliveDelay): p := o.MakeNop() _, err := p.Send(client.connection) if err != nil { o.Warn("Client %s: error sending packet: %s", client.Name(), err) client.Abort() } } } client.connection.Close() }
func handleIllegal(c net.Conn, message interface{}) { o.Fail("Received illegal message") }
func handleAudienceRequest(c net.Conn) { defer c.Close() c.SetTimeout(0) r, _ := c.(io.Reader) w, _ := c.(io.Writer) dec := json.NewDecoder(r) enc := json.NewEncoder(w) outobj := new(GenericJsonRequest) err := dec.Decode(outobj) if err != nil { o.Warn("Error decoding JSON talking to audience: %s", err) return } if nil == outobj.Op { o.Warn("Malformed JSON message talking to audience. Missing Op") return } switch *(outobj.Op) { case "status": if nil == outobj.Id { o.Warn("Malformed Status message talking to audience. Missing Job ID") return } job := o.JobGet(*outobj.Id) jresp := new([2]interface{}) if nil != job { jresp[0] = "OK" iresp := NewJsonStatusResponse() switch job.State { case o.JOB_PENDING: iresp.Status = "PENDING" case o.JOB_SUCCESSFUL: iresp.Status = "OK" case o.JOB_FAILED_PARTIAL: iresp.Status = "PARTIAL_FAIL" case o.JOB_FAILED: iresp.Status = "FAIL" default: o.Fail("Blargh. %d is an unknown job state!", job.State) } resnames := o.JobGetResultNames(*outobj.Id) for i := range resnames { tr := o.JobGetResult(*outobj.Id, resnames[i]) if nil != tr { presp := NewJsonPlayerStatus() switch tr.State { case o.RESP_RUNNING: presp.Status = "PENDING" case o.RESP_FINISHED: presp.Status = "OK" case o.RESP_FAILED: presp.Status = "FAIL" case o.RESP_FAILED_UNKNOWN_SCORE: presp.Status = "UNK_SCORE" case o.RESP_FAILED_HOST_ERROR: presp.Status = "HOST_ERROR" case o.RESP_FAILED_UNKNOWN: presp.Status = "UNKNOWN_FAILURE" } for k, v := range tr.Response { presp.Response[k] = v } iresp.Players[resnames[i]] = presp } } jresp[1] = iresp } else { jresp[0] = "Error" jresp[1] = nil } enc.Encode(jresp) o.Debug("Status...") case "queue": if nil == outobj.Score { o.Warn("Malformed Queue message talking to audience. Missing Score") sendQueueFailureResponse("Missing Score", enc) return } if nil == outobj.Scope { o.Warn("Malformed Queue message talking to audience. Missing Scope") sendQueueFailureResponse("Missing Scope", enc) return } if nil == outobj.Players || len(outobj.Players) < 1 { o.Warn("Malformed Queue message talking to audience. Missing Players") sendQueueFailureResponse("Missing Players", enc) return } for _, player := range outobj.Players { if !HostAuthorised(player) { o.Warn("Malformed Queue message - unknown player %s specified.", player) sendQueueFailureResponse("Invalid Player", enc) return } } job := NewRequest() job.Score = *outobj.Score switch *outobj.Scope { case "one": job.Scope = o.SCOPE_ONEOF case "all": job.Scope = o.SCOPE_ALLOF default: sendQueueFailureResponse("Invalid Scope", enc) return } job.Players = outobj.Players job.Params = outobj.Params QueueJob(job) sendQueueSuccessResponse(job, enc) default: o.Warn("Unknown operation talking to audience: \"%s\"", *(outobj.Op)) return } _ = enc }