1/* 2 * Copyright 2001-2010, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ithamar R. Adema 7 * Michael Pfeiffer 8 */ 9#include "Printer.h" 10 11#include "BeUtils.h" 12#include "pr_server.h" 13#include "PrintAddOnServer.h" 14#include "PrintServerApp.h" 15 16 // posix 17#include <limits.h> 18#include <stdlib.h> 19#include <string.h> 20#include <unistd.h> 21 22 // BeOS API 23#include <Application.h> 24#include <Autolock.h> 25#include <Message.h> 26#include <NodeMonitor.h> 27#include <String.h> 28#include <StorageKit.h> 29#include <SupportDefs.h> 30 31 32SpoolFolder::SpoolFolder(BLocker* locker, BLooper* looper, 33 const BDirectory& spoolDir) 34 : Folder(locker, looper, spoolDir) 35{ 36} 37 38 39// Notify print_server that there is a job file waiting for printing 40void 41SpoolFolder::Notify(Job* job, int kind) 42{ 43 if ((kind == kJobAdded || kind == kJobAttrChanged) 44 && job->IsValid() && job->IsWaiting()) { 45 be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB); 46 } 47} 48 49 50// --------------------------------------------------------------- 51BObjectList<Printer> Printer::sPrinters; 52 53 54// --------------------------------------------------------------- 55// Find [static] 56// 57// Searches the static object list for a printer object with the 58// specified name. 59// 60// Parameters: 61// name - Printer definition name we're looking for. 62// 63// Returns: 64// Pointer to Printer object, or NULL if not found. 65// --------------------------------------------------------------- 66Printer* 67Printer::Find(const BString& name) 68{ 69 // Look in list to find printer definition 70 for (int32 idx = 0; idx < sPrinters.CountItems(); idx++) { 71 if (name == sPrinters.ItemAt(idx)->Name()) 72 return sPrinters.ItemAt(idx); 73 } 74 return NULL; 75} 76 77 78Printer* 79Printer::Find(node_ref* node) 80{ 81 node_ref n; 82 // Look in list to find printer definition 83 for (int32 idx = 0; idx < sPrinters.CountItems(); idx++) { 84 Printer* printer = sPrinters.ItemAt(idx); 85 printer->SpoolDir()->GetNodeRef(&n); 86 if (n == *node) 87 return printer; 88 } 89 90 // None found, so return NULL 91 return NULL; 92} 93 94 95Printer* 96Printer::At(int32 idx) 97{ 98 return sPrinters.ItemAt(idx); 99} 100 101 102void 103Printer::Remove(Printer* printer) 104{ 105 sPrinters.RemoveItem(printer); 106} 107 108 109int32 110Printer::CountPrinters() 111{ 112 return sPrinters.CountItems(); 113} 114 115 116// --------------------------------------------------------------- 117// Printer [constructor] 118// 119// Initializes the printer object with data read from the 120// attributes attached to the printer definition node. 121// 122// Parameters: 123// node - Printer definition node for this printer. 124// 125// Returns: 126// none. 127// --------------------------------------------------------------- 128Printer::Printer(const BDirectory* node, Resource* res) 129 : Inherited(B_EMPTY_STRING), 130 fPrinter(gLock, be_app, *node), 131 fResource(res), 132 fSinglePrintThread(res->NeedsLocking()), 133 fJob(NULL), 134 fProcessing(0), 135 fAbort(false) 136{ 137 BString name; 138 // Set our name to the name of the passed node 139 if (SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name) == B_OK) 140 SetName(name.String()); 141 142 // Add us to the global list of known printer definitions 143 sPrinters.AddItem(this); 144 145 ResetJobStatus(); 146} 147 148 149Printer::~Printer() 150{ 151 ((PrintServerApp*)be_app)->NotifyPrinterDeletion(this); 152} 153 154 155void 156Printer::MessageReceived(BMessage* msg) 157{ 158 switch(msg->what) { 159 case B_GET_PROPERTY: 160 case B_SET_PROPERTY: 161 case B_CREATE_PROPERTY: 162 case B_DELETE_PROPERTY: 163 case B_COUNT_PROPERTIES: 164 case B_EXECUTE_PROPERTY: 165 HandleScriptingCommand(msg); 166 break; 167 168 default: 169 Inherited::MessageReceived(msg); 170 } 171} 172 173 174// Remove printer spooler directory 175status_t 176Printer::Remove() 177{ 178 status_t rc = B_OK; 179 BPath path; 180 181 if ((rc = ::find_directory(B_USER_PRINTERS_DIRECTORY, &path)) == B_OK) { 182 path.Append(Name()); 183 rc = rmdir(path.Path()); 184 } 185 186 return rc; 187} 188 189 190status_t 191Printer::FindPathToDriver(const char* driverName, BPath* path) 192{ 193 return PrintAddOnServer::FindPathToDriver(driverName, path); 194} 195 196 197// --------------------------------------------------------------- 198// ConfigurePrinter 199// 200// Handles calling the printer addon's add_printer function. 201// 202// Parameters: 203// driverName - the name of the printer driver add-on 204// printerName - the name of the printer spool folder 205// 206// Returns: 207// B_OK if successful or errorcode otherwise. 208// --------------------------------------------------------------- 209status_t 210Printer::ConfigurePrinter(const char* driverName, 211 const char* printerName) 212{ 213 PrintAddOnServer addOn(driverName); 214 return addOn.AddPrinter(printerName); 215} 216 217 218// --------------------------------------------------------------- 219// ConfigurePage 220// 221// Handles calling the printer addon's config_page function. 222// 223// Parameters: 224// settings - Page settings to display. The contents of this 225// message will be replaced with the new settings 226// if the function returns success. 227// 228// Returns: 229// B_OK if successful or errorcode otherwise. 230// --------------------------------------------------------------- 231status_t 232Printer::ConfigurePage(BMessage& settings) 233{ 234 BString driver; 235 status_t result = GetDriverName(&driver); 236 if (result != B_OK) 237 return result; 238 239 PrintAddOnServer addOn(driver.String()); 240 result = addOn.ConfigPage(SpoolDir(), &settings); 241 if (result == B_OK) { 242 AddCurrentPrinter(settings); 243 } 244 return result; 245} 246 247 248// --------------------------------------------------------------- 249// ConfigureJob 250// 251// Handles calling the printer addon's config_job function. 252// 253// Parameters: 254// settings - Job settings to display. The contents of this 255// message will be replaced with the new settings 256// if the function returns success. 257// 258// Returns: 259// B_OK if successful or errorcode otherwise. 260// --------------------------------------------------------------- 261status_t 262Printer::ConfigureJob(BMessage& settings) 263{ 264 BString driver; 265 status_t result = GetDriverName(&driver); 266 if (result != B_OK) 267 return result; 268 269 PrintAddOnServer addOn(driver.String()); 270 result = addOn.ConfigJob(SpoolDir(), &settings); 271 if (result == B_OK) 272 AddCurrentPrinter(settings); 273 274 return result; 275} 276 277 278// --------------------------------------------------------------- 279// HandleSpooledJobs 280// 281// Print spooled jobs in a new thread. 282// --------------------------------------------------------------- 283void 284Printer::HandleSpooledJob() 285{ 286 BAutolock lock(gLock); 287 if (lock.IsLocked() 288 && (!fSinglePrintThread || fProcessing == 0) && FindSpooledJob()) { 289 StartPrintThread(); 290 } 291} 292 293 294// --------------------------------------------------------------- 295// GetDefaultSettings 296// 297// Retrieve the default configuration message from printer add-on 298// 299// Parameters: 300// settings, output paramter. 301// 302// Returns: 303// B_OK if successful or errorcode otherwise. 304// --------------------------------------------------------------- 305status_t 306Printer::GetDefaultSettings(BMessage& settings) 307{ 308 BString driver; 309 status_t result = GetDriverName(&driver); 310 if (result != B_OK) 311 return result; 312 313 PrintAddOnServer addOn(driver.String()); 314 result = addOn.DefaultSettings(SpoolDir(), &settings); 315 if (result == B_OK) 316 AddCurrentPrinter(settings); 317 318 return result; 319} 320 321 322void 323Printer::AbortPrintThread() 324{ 325 fAbort = true; 326} 327 328 329status_t 330Printer::GetDriverName(BString* name) 331{ 332 return SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_DRV_NAME, name); 333} 334 335 336// --------------------------------------------------------------- 337// AddCurrentPrinter 338// 339// Add printer name to message. 340// 341// Parameters: 342// msg - message. 343// --------------------------------------------------------------- 344void 345Printer::AddCurrentPrinter(BMessage& message) 346{ 347 BString name; 348 GetName(name); 349 350 message.RemoveName(PSRV_FIELD_CURRENT_PRINTER); 351 message.AddString(PSRV_FIELD_CURRENT_PRINTER, name.String()); 352} 353 354 355// --------------------------------------------------------------- 356// GetName 357// 358// Get the name from spool directory. 359// 360// Parameters: 361// name - the name of the printer. 362// --------------------------------------------------------------- 363void 364Printer::GetName(BString& name) 365{ 366 if (SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name) != B_OK) 367 name = "Unknown Printer"; 368} 369 370 371// --------------------------------------------------------------- 372// ResetJobStatus 373// 374// Reset status of "processing" jobs to "waiting" at print_server start. 375// --------------------------------------------------------------- 376void 377Printer::ResetJobStatus() 378{ 379 if (fPrinter.Lock()) { 380 const int32 n = fPrinter.CountJobs(); 381 for (int32 i = 0; i < n; i ++) { 382 Job* job = fPrinter.JobAt(i); 383 if (job->Status() == kProcessing) 384 job->SetStatus(kWaiting); 385 } 386 fPrinter.Unlock(); 387 } 388} 389 390 391// --------------------------------------------------------------- 392// HasCurrentPrinter 393// 394// Try to read the printer name from job file. 395// 396// Parameters: 397// name - the printer name. 398// 399// Returns: 400// true if successful. 401// --------------------------------------------------------------- 402bool 403Printer::HasCurrentPrinter(BString& name) 404{ 405 BMessage settings; 406 // read settings from spool file and get printer name 407 BFile jobFile(&fJob->EntryRef(), B_READ_WRITE); 408 return jobFile.InitCheck() == B_OK 409 && jobFile.Seek(sizeof(print_file_header), SEEK_SET) == sizeof(print_file_header) 410 && settings.Unflatten(&jobFile) == B_OK 411 && settings.FindString(PSRV_FIELD_CURRENT_PRINTER, &name) == B_OK; 412} 413 414 415// --------------------------------------------------------------- 416// MoveJob 417// 418// Try to move job to another printer folder. 419// 420// Parameters: 421// name - the printer folder name. 422// 423// Returns: 424// true if successful. 425// --------------------------------------------------------------- 426bool 427Printer::MoveJob(const BString& name) 428{ 429 BPath file(&fJob->EntryRef()); 430 BPath path; 431 file.GetParent(&path); 432 path.Append(".."); 433 path.Append(name.String()); 434 BDirectory dir(path.Path()); 435 BEntry entry(&fJob->EntryRef()); 436 // try to move job file to proper directory 437 return entry.MoveTo(&dir) == B_OK; 438} 439 440 441// --------------------------------------------------------------- 442// FindSpooledJob 443// 444// Looks if there is a job waiting to be processed and moves 445// jobs to the proper printer folder. 446// 447// Note: 448// Our implementation of BPrintJob moves jobs to the 449// proper printer folder. 450// 451// 452// Returns: 453// true if there is a job present in fJob. 454// --------------------------------------------------------------- 455bool 456Printer::FindSpooledJob() 457{ 458 BString name2; 459 GetName(name2); 460 do { 461 fJob = fPrinter.GetNextJob(); 462 if (fJob) { 463 BString name; 464 if (HasCurrentPrinter(name) && name != name2 && MoveJob(name)) { 465 // job in wrong printer folder moved to apropriate one 466 fJob->SetStatus(kUnknown, false); // so that fPrinter.GetNextJob skips it 467 fJob->Release(); 468 } else { 469 // job found 470 fJob->SetPrinter(this); 471 return true; 472 } 473 } 474 } while (fJob != NULL); 475 return false; 476} 477 478 479// --------------------------------------------------------------- 480// PrintSpooledJob 481// 482// Loads the printer add-on and calls its take_job function with 483// the spool file as argument. 484// 485// Parameters: 486// spoolFile - the path to the spool file. 487// 488// Returns: 489// B_OK if successful. 490// --------------------------------------------------------------- 491status_t 492Printer::PrintSpooledJob(const char* spoolFile) 493{ 494 BString driver; 495 status_t result = GetDriverName(&driver); 496 if (result != B_OK) 497 return result; 498 499 PrintAddOnServer addOn(driver.String()); 500 return addOn.TakeJob(spoolFile, SpoolDir()); 501} 502 503 504// --------------------------------------------------------------- 505// PrintThread 506// 507// Loads the printer add-on and calls its take_job function with 508// the spool file as argument. 509// 510// Parameters: 511// job - the spool job. 512// --------------------------------------------------------------- 513void 514Printer::PrintThread(Job* job) 515{ 516 // Wait until resource is available 517 fResource->Lock(); 518 bool failed = true; 519 // Can we continue? 520 if (!fAbort) { 521 BPath path; 522 bool canOpenFile; 523 { 524 BEntry entry(&job->EntryRef()); 525 path.SetTo(&entry); 526 BFile jobFile(path.Path(), B_READ_WRITE); 527 canOpenFile = jobFile.InitCheck() == B_OK; 528 } 529 // Tell the printer to print the spooled job 530 if (canOpenFile && PrintSpooledJob(path.Path()) == B_OK) { 531 // Remove spool file if printing was successful. 532 job->Remove(); failed = false; 533 } 534 } 535 // Set status of spooled job on error 536 if (failed) 537 job->SetStatus(kFailed); 538 fResource->Unlock(); 539 job->Release(); 540 atomic_add(&fProcessing, -1); 541 Release(); 542 // Notify print_server to process next spooled job 543 be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB); 544} 545 546 547// --------------------------------------------------------------- 548// print_thread 549// 550// Print thread entry, calls PrintThread with spool job. 551// 552// Parameters: 553// data - spool job. 554// 555// Returns: 556// 0 always. 557// --------------------------------------------------------------- 558status_t 559Printer::print_thread(void* data) 560{ 561 Job* job = static_cast<Job*>(data); 562 job->GetPrinter()->PrintThread(job); 563 return 0; 564} 565 566 567// --------------------------------------------------------------- 568// StartPrintThread 569// 570// Sets the status of the current spool job to "processing" and 571// starts the print_thread. 572// --------------------------------------------------------------- 573void 574Printer::StartPrintThread() 575{ 576 Acquire(); 577 thread_id tid = spawn_thread(print_thread, "print", B_NORMAL_PRIORITY, (void*)fJob); 578 if (tid > 0) { 579 fJob->SetStatus(kProcessing); 580 atomic_add(&fProcessing, 1); 581 resume_thread(tid); 582 } else { 583 fJob->Release(); 584 Release(); 585 } 586} 587 588