// processJob performs these steps: // // 1) Assembles the job resources (printer, ticket, data) // 2) Creates a new job in CUPS. // 3) Follows up with the job state until done or error. // 4) Deletes temporary file. // // Nothing is returned; intended for use as goroutine. func (gcp *GoogleCloudPrint) processJob(job *Job, printer *lib.Printer, reportJobFailed func()) { log.InfoJobf(job.GCPJobID, "Received from cloud") ticket, filename, message, state := gcp.assembleJob(job) if message != "" { reportJobFailed() log.ErrorJob(job.GCPJobID, message) if err := gcp.Control(job.GCPJobID, state); err != nil { log.ErrorJob(job.GCPJobID, err) } return } gcp.jobs <- &lib.Job{ NativePrinterName: printer.Name, Filename: filename, Title: job.Title, User: job.OwnerID, JobID: job.GCPJobID, Ticket: ticket, UpdateJob: gcp.Control, } }
// printJob prints a new job to a CUPS printer, then polls the CUPS job state // and updates the GCP/Privet job state. then returns when the job state is DONE // or ABORTED. // // All errors are reported and logged from inside this function. func (pm *PrinterManager) printJob(cupsPrinterName, filename, title, user, jobID string, ticket *cdd.CloudJobTicket, updateJob func(string, cdd.PrintJobStateDiff) error) { defer os.Remove(filename) if !pm.addInFlightJob(jobID) { // This print job was already received. We probably received it // again because the first instance is still QUEUED (ie not // IN_PROGRESS). That's OK, just throw away the second instance. return } defer pm.deleteInFlightJob(jobID) if !pm.jobFullUsername { user = strings.Split(user, "@")[0] } printer, exists := pm.printers.GetByCUPSName(cupsPrinterName) if !exists { pm.incrementJobsProcessed(false) state := cdd.PrintJobStateDiff{ State: &cdd.JobState{ Type: cdd.JobStateAborted, ServiceActionCause: &cdd.ServiceActionCause{ErrorCode: cdd.ServiceActionCausePrinterDeleted}, }, } if err := updateJob(jobID, state); err != nil { log.ErrorJob(jobID, err) } return } cupsJobID, err := pm.cups.Print(&printer, filename, title, user, jobID, ticket) if err != nil { pm.incrementJobsProcessed(false) log.ErrorJobf(jobID, "Failed to submit to CUPS: %s", err) state := cdd.PrintJobStateDiff{ State: &cdd.JobState{ Type: cdd.JobStateAborted, DeviceActionCause: &cdd.DeviceActionCause{ErrorCode: cdd.DeviceActionCausePrintFailure}, }, } if err := updateJob(jobID, state); err != nil { log.ErrorJob(jobID, err) } return } log.InfoJobf(jobID, "Submitted as CUPS job %d", cupsJobID) var state cdd.PrintJobStateDiff ticker := time.NewTicker(time.Second) defer ticker.Stop() for _ = range ticker.C { cupsState, err := pm.cups.GetJobState(cupsJobID) if err != nil { log.WarningJobf(jobID, "Failed to get state of CUPS job %d: %s", cupsJobID, err) state = cdd.PrintJobStateDiff{ State: &cdd.JobState{ Type: cdd.JobStateAborted, DeviceActionCause: &cdd.DeviceActionCause{ErrorCode: cdd.DeviceActionCauseOther}, }, PagesPrinted: state.PagesPrinted, } if err := updateJob(jobID, state); err != nil { log.ErrorJob(jobID, err) } pm.incrementJobsProcessed(false) return } if !reflect.DeepEqual(cupsState, state) { state = cupsState if err = updateJob(jobID, state); err != nil { log.ErrorJob(jobID, err) } log.InfoJobf(jobID, "State: %s", state.State.Type) } if state.State.Type != cdd.JobStateInProgress { if state.State.Type == cdd.JobStateDone { pm.incrementJobsProcessed(true) } else { pm.incrementJobsProcessed(false) } return } } }