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 32 SpoolFolder::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 40 void 41 SpoolFolder::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 // --------------------------------------------------------------- 51 BObjectList<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 // --------------------------------------------------------------- 66 Printer* 67 Printer::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 78 Printer* 79 Printer::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 95 Printer* 96 Printer::At(int32 idx) 97 { 98 return sPrinters.ItemAt(idx); 99 } 100 101 102 void 103 Printer::Remove(Printer* printer) 104 { 105 sPrinters.RemoveItem(printer); 106 } 107 108 109 int32 110 Printer::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 // --------------------------------------------------------------- 128 Printer::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 149 Printer::~Printer() 150 { 151 ((PrintServerApp*)be_app)->NotifyPrinterDeletion(this); 152 } 153 154 155 void 156 Printer::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 175 status_t 176 Printer::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 190 status_t 191 Printer::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 // --------------------------------------------------------------- 209 status_t 210 Printer::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 // --------------------------------------------------------------- 231 status_t 232 Printer::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 // --------------------------------------------------------------- 261 status_t 262 Printer::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 // --------------------------------------------------------------- 283 void 284 Printer::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 // --------------------------------------------------------------- 305 status_t 306 Printer::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 322 void 323 Printer::AbortPrintThread() 324 { 325 fAbort = true; 326 } 327 328 329 status_t 330 Printer::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 // --------------------------------------------------------------- 344 void 345 Printer::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 // --------------------------------------------------------------- 363 void 364 Printer::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 // --------------------------------------------------------------- 376 void 377 Printer::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 // --------------------------------------------------------------- 402 bool 403 Printer::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 // --------------------------------------------------------------- 426 bool 427 Printer::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 // --------------------------------------------------------------- 455 bool 456 Printer::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 // --------------------------------------------------------------- 491 status_t 492 Printer::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 // --------------------------------------------------------------- 513 void 514 Printer::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 // --------------------------------------------------------------- 558 status_t 559 Printer::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 // --------------------------------------------------------------- 573 void 574 Printer::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