1 /*****************************************************************************/ 2 // print_server Background Application. 3 // 4 // Version: 1.0.0d1 5 // 6 // The print_server manages the communication between applications and the 7 // printer and transport drivers. 8 // 9 // Authors 10 // Ithamar R. Adema 11 // Michael Pfeiffer 12 // 13 // This application and all source files used in its construction, except 14 // where noted, are licensed under the MIT License, and have been written 15 // and are: 16 // 17 // Copyright (c) 2001,2002 OpenBeOS Project 18 // 19 // Permission is hereby granted, free of charge, to any person obtaining a 20 // copy of this software and associated documentation files (the "Software"), 21 // to deal in the Software without restriction, including without limitation 22 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 23 // and/or sell copies of the Software, and to permit persons to whom the 24 // Software is furnished to do so, subject to the following conditions: 25 // 26 // The above copyright notice and this permission notice shall be included 27 // in all copies or substantial portions of the Software. 28 // 29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 30 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 32 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 // DEALINGS IN THE SOFTWARE. 36 /*****************************************************************************/ 37 38 #include "Printer.h" 39 40 #include "pr_server.h" 41 #include "BeUtils.h" 42 #include "PrintServerApp.h" 43 44 // posix 45 #include <limits.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 // BeOS API 51 #include <Application.h> 52 #include <Autolock.h> 53 #include <Message.h> 54 #include <NodeMonitor.h> 55 #include <String.h> 56 #include <StorageKit.h> 57 #include <SupportDefs.h> 58 59 60 SpoolFolder::SpoolFolder(BLocker*locker, BLooper* looper, const BDirectory& spoolDir) 61 : Folder(locker, looper, spoolDir) 62 { 63 } 64 65 66 // Notify print_server that there is a job file waiting for printing 67 void SpoolFolder::Notify(Job* job, int kind) 68 { 69 if ((kind == kJobAdded || kind == kJobAttrChanged) && 70 job->IsValid() && job->IsWaiting()) { 71 be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB); 72 } 73 } 74 75 76 // --------------------------------------------------------------- 77 typedef BMessage* (*config_func_t)(BNode*, const BMessage*); 78 typedef BMessage* (*take_job_func_t)(BFile*, BNode*, const BMessage*); 79 typedef char* (*add_printer_func_t)(const char* printer_name); 80 typedef BMessage* (*default_settings_t)(BNode*); 81 82 // --------------------------------------------------------------- 83 BObjectList<Printer> Printer::sPrinters; 84 85 86 // --------------------------------------------------------------- 87 // Find [static] 88 // 89 // Searches the static object list for a printer object with the 90 // specified name. 91 // 92 // Parameters: 93 // name - Printer definition name we're looking for. 94 // 95 // Returns: 96 // Pointer to Printer object, or NULL if not found. 97 // --------------------------------------------------------------- 98 Printer* Printer::Find(const BString& name) 99 { 100 // Look in list to find printer definition 101 for (int32 idx=0; idx < sPrinters.CountItems(); idx++) { 102 if (name == sPrinters.ItemAt(idx)->Name()) { 103 return sPrinters.ItemAt(idx); 104 } 105 } 106 107 // None found, so return NULL 108 return NULL; 109 } 110 111 Printer* Printer::Find(node_ref* node) 112 { 113 node_ref n; 114 // Look in list to find printer definition 115 for (int32 idx=0; idx < sPrinters.CountItems(); idx++) { 116 Printer* printer = sPrinters.ItemAt(idx); 117 printer->SpoolDir()->GetNodeRef(&n); 118 if (n == *node) return printer; 119 } 120 121 // None found, so return NULL 122 return NULL; 123 } 124 125 Printer* Printer::At(int32 idx) 126 { 127 return sPrinters.ItemAt(idx); 128 } 129 130 void Printer::Remove(Printer* printer) { 131 sPrinters.RemoveItem(printer); 132 } 133 134 int32 Printer::CountPrinters() 135 { 136 return sPrinters.CountItems(); 137 } 138 139 // --------------------------------------------------------------- 140 // Printer [constructor] 141 // 142 // Initializes the printer object with data read from the 143 // attributes attached to the printer definition node. 144 // 145 // Parameters: 146 // node - Printer definition node for this printer. 147 // 148 // Returns: 149 // none. 150 // --------------------------------------------------------------- 151 Printer::Printer(const BDirectory* node, Resource* res) 152 : Inherited(B_EMPTY_STRING), 153 fPrinter(gLock, be_app, *node), 154 fResource(res), 155 fSinglePrintThread(true), 156 fJob(NULL), 157 fProcessing(0), 158 fAbort(false) 159 { 160 BString name; 161 // Set our name to the name of the passed node 162 if (SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name) == B_OK) 163 SetName(name.String()); 164 165 if (name == "Preview") fSinglePrintThread = false; 166 167 // Add us to the global list of known printer definitions 168 sPrinters.AddItem(this); 169 170 ResetJobStatus(); 171 } 172 173 Printer::~Printer() 174 { 175 ((PrintServerApp*)be_app)->NotifyPrinterDeletion(this); 176 } 177 178 179 // Remove printer spooler directory 180 status_t Printer::Remove() 181 { 182 status_t rc = B_OK; 183 BPath path; 184 185 if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY, &path)) == B_OK) { 186 path.Append(Name()); 187 rc = rmdir(path.Path()); 188 } 189 190 return rc; 191 } 192 193 // --------------------------------------------------------------- 194 // ConfigurePrinter 195 // 196 // Handles calling the printer addon's add_printer function. 197 // 198 // Parameters: 199 // none. 200 // 201 // Returns: 202 // B_OK if successful or errorcode otherwise. 203 // --------------------------------------------------------------- 204 status_t Printer::ConfigurePrinter() 205 { 206 image_id id; 207 status_t rc; 208 209 if ((rc=LoadPrinterAddon(id)) == B_OK) { 210 // Addon was loaded, so try and get the add_printer symbol 211 add_printer_func_t func; 212 213 if (get_image_symbol(id, "add_printer", B_SYMBOL_TYPE_TEXT, (void**)&func) == B_OK) { 214 // call the function and check its result 215 rc = ((*func)(Name()) == NULL) ? B_ERROR : B_OK; 216 } 217 218 ::unload_add_on(id); 219 } 220 221 return rc; 222 } 223 224 // --------------------------------------------------------------- 225 // ConfigurePage 226 // 227 // Handles calling the printer addon's config_page function. 228 // 229 // Parameters: 230 // settings - Page settings to display. The contents of this 231 // message will be replaced with the new settings 232 // if the function returns success. 233 // 234 // Returns: 235 // B_OK if successful or errorcode otherwise. 236 // --------------------------------------------------------------- 237 status_t Printer::ConfigurePage(BMessage& settings) 238 { 239 image_id id; 240 status_t rc; 241 242 if ((rc=LoadPrinterAddon(id)) == B_OK) { 243 // Addon was loaded, so try and get the config_page symbol 244 config_func_t func; 245 246 if ((rc=get_image_symbol(id, "config_page", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 247 // call the function and check its result 248 BMessage* new_settings = (*func)(SpoolDir(), &settings); 249 if (new_settings != NULL && new_settings->what != 'baad') { 250 settings = *new_settings; 251 AddCurrentPrinter(&settings); 252 } else 253 rc = B_ERROR; 254 delete new_settings; 255 } 256 257 ::unload_add_on(id); 258 } 259 260 return rc; 261 } 262 263 // --------------------------------------------------------------- 264 // ConfigureJob 265 // 266 // Handles calling the printer addon's config_job function. 267 // 268 // Parameters: 269 // settings - Job settings to display. The contents of this 270 // message will be replaced with the new settings 271 // if the function returns success. 272 // 273 // Returns: 274 // B_OK if successful or errorcode otherwise. 275 // --------------------------------------------------------------- 276 status_t Printer::ConfigureJob(BMessage& settings) 277 { 278 image_id id; 279 status_t rc; 280 281 if ((rc=LoadPrinterAddon(id)) == B_OK) { 282 // Addon was loaded, so try and get the config_job symbol 283 config_func_t func; 284 285 if ((rc=get_image_symbol(id, "config_job", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 286 // call the function and check its result 287 BMessage* new_settings = (*func)(SpoolDir(), &settings); 288 if ((new_settings != NULL) && (new_settings->what != 'baad')) { 289 settings = *new_settings; 290 AddCurrentPrinter(&settings); 291 } else 292 rc = B_ERROR; 293 delete new_settings; 294 } 295 296 ::unload_add_on(id); 297 } 298 299 return rc; 300 } 301 302 303 // --------------------------------------------------------------- 304 // HandleSpooledJobs 305 // 306 // Print spooled jobs in a new thread. 307 // --------------------------------------------------------------- 308 void Printer::HandleSpooledJob() { 309 BAutolock lock(gLock); 310 if (lock.IsLocked() && (!fSinglePrintThread || fProcessing == 0) && FindSpooledJob()) { 311 StartPrintThread(); 312 } 313 } 314 315 316 // --------------------------------------------------------------- 317 // GetDefaultSettings 318 // 319 // Retrieve the default configuration message from printer add-on 320 // 321 // Parameters: 322 // settings, output paramter. 323 // 324 // Returns: 325 // B_OK if successful or errorcode otherwise. 326 // --------------------------------------------------------------- 327 status_t Printer::GetDefaultSettings(BMessage& settings) { 328 image_id id; 329 status_t rc; 330 331 if ((rc=LoadPrinterAddon(id)) == B_OK) { 332 // Addon was loaded, so try and get the default_settings symbol 333 default_settings_t func; 334 335 if ((rc=get_image_symbol(id, "default_settings", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 336 // call the function and check its result 337 BMessage* new_settings = (*func)(SpoolDir()); 338 if (new_settings) { 339 settings = *new_settings; 340 AddCurrentPrinter(&settings); 341 } else { 342 rc = B_ERROR; 343 } 344 delete new_settings; 345 } 346 347 ::unload_add_on(id); 348 } 349 350 return rc; 351 } 352 353 354 void Printer::AbortPrintThread() { 355 fAbort = true; 356 } 357 358 // --------------------------------------------------------------- 359 // LoadPrinterAddon 360 // 361 // Try to load the printer addon into memory. 362 // 363 // Parameters: 364 // id - image_id set to the image id of the loaded addon. 365 // 366 // Returns: 367 // B_OK if successful or errorcode otherwise. 368 // --------------------------------------------------------------- 369 status_t Printer::LoadPrinterAddon(image_id& id) 370 { 371 BString drName; 372 status_t rc; 373 BPath path; 374 375 if ((rc=SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_DRV_NAME, &drName)) == B_OK) { 376 // try to locate the driver 377 if ((rc=::TestForAddonExistence(drName.String(), B_USER_ADDONS_DIRECTORY, "Print", path)) != B_OK) { 378 if ((rc=::TestForAddonExistence(drName.String(), B_COMMON_ADDONS_DIRECTORY, "Print", path)) != B_OK) { 379 rc = ::TestForAddonExistence(drName.String(), B_BEOS_ADDONS_DIRECTORY, "Print", path); 380 } 381 } 382 383 // If the driver was found 384 if (rc == B_OK) { 385 // If we cannot load the addon 386 if ((id=::load_add_on(path.Path())) < 0) 387 rc = id; 388 } 389 } 390 return rc; 391 } 392 393 394 // --------------------------------------------------------------- 395 // AddCurrentPrinter 396 // 397 // Add printer name to message. 398 // 399 // Parameters: 400 // msg - message. 401 // --------------------------------------------------------------- 402 void Printer::AddCurrentPrinter(BMessage* msg) 403 { 404 BString name; 405 GetName(name); 406 if (msg->HasString(PSRV_FIELD_CURRENT_PRINTER)) { 407 msg->ReplaceString(PSRV_FIELD_CURRENT_PRINTER, name.String()); 408 } else { 409 msg->AddString(PSRV_FIELD_CURRENT_PRINTER, name.String()); 410 } 411 } 412 413 414 // --------------------------------------------------------------- 415 // MessageReceived 416 // 417 // Handle scripting messages. 418 // 419 // Parameters: 420 // msg - message. 421 // --------------------------------------------------------------- 422 void Printer::MessageReceived(BMessage* msg) 423 { 424 switch(msg->what) { 425 case B_GET_PROPERTY: 426 case B_SET_PROPERTY: 427 case B_CREATE_PROPERTY: 428 case B_DELETE_PROPERTY: 429 case B_COUNT_PROPERTIES: 430 case B_EXECUTE_PROPERTY: 431 HandleScriptingCommand(msg); 432 break; 433 434 default: 435 Inherited::MessageReceived(msg); 436 } 437 } 438 439 // --------------------------------------------------------------- 440 // GetName 441 // 442 // Get the name from spool directory. 443 // 444 // Parameters: 445 // name - the name of the printer. 446 // --------------------------------------------------------------- 447 void Printer::GetName(BString& name) { 448 if (B_OK != SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name)) 449 name = "Unknown Printer"; 450 } 451 452 // --------------------------------------------------------------- 453 // ResetJobStatus 454 // 455 // Reset status of "processing" jobs to "waiting" at print_server start. 456 // --------------------------------------------------------------- 457 void Printer::ResetJobStatus() { 458 if (fPrinter.Lock()) { 459 const int32 n = fPrinter.CountJobs(); 460 for (int32 i = 0; i < n; i ++) { 461 Job* job = fPrinter.JobAt(i); 462 if (job->Status() == kProcessing) job->SetStatus(kWaiting); 463 } 464 fPrinter.Unlock(); 465 } 466 } 467 468 // --------------------------------------------------------------- 469 // HasCurrentPrinter 470 // 471 // Try to read the printer name from job file. 472 // 473 // Parameters: 474 // name - the printer name. 475 // 476 // Returns: 477 // true if successful. 478 // --------------------------------------------------------------- 479 bool Printer::HasCurrentPrinter(BString& name) { 480 BMessage settings; 481 // read settings from spool file and get printer name 482 BFile jobFile(&fJob->EntryRef(), B_READ_WRITE); 483 return jobFile.InitCheck() == B_OK && 484 jobFile.Seek(sizeof(print_file_header), SEEK_SET) == sizeof(print_file_header) && 485 settings.Unflatten(&jobFile) == B_OK && 486 settings.FindString(PSRV_FIELD_CURRENT_PRINTER, &name) == B_OK; 487 } 488 489 // --------------------------------------------------------------- 490 // MoveJob 491 // 492 // Try to move job to another printer folder. 493 // 494 // Parameters: 495 // name - the printer folder name. 496 // 497 // Returns: 498 // true if successful. 499 // --------------------------------------------------------------- 500 bool Printer::MoveJob(const BString& name) { 501 BPath file(&fJob->EntryRef()); 502 BPath path; 503 file.GetParent(&path); 504 path.Append(".."); path.Append(name.String()); 505 BDirectory dir(path.Path()); 506 BEntry entry(&fJob->EntryRef()); 507 // try to move job file to proper directory 508 return entry.MoveTo(&dir) == B_OK; 509 } 510 511 // --------------------------------------------------------------- 512 // FindSpooledJob 513 // 514 // Looks if there is a job waiting to be processed and moves 515 // jobs to the proper printer folder. 516 // 517 // Note: 518 // Our implementation of BPrintJob moves jobs to the 519 // proper printer folder. 520 // 521 // 522 // Returns: 523 // true if there is a job present in fJob. 524 // --------------------------------------------------------------- 525 bool Printer::FindSpooledJob() { 526 BString name2; 527 GetName(name2); 528 do { 529 fJob = fPrinter.GetNextJob(); 530 if (fJob) { 531 BString name; 532 if (HasCurrentPrinter(name) && name != name2 && MoveJob(name)) { 533 // job in wrong printer folder moved to apropriate one 534 fJob->SetStatus(kUnknown, false); // so that fPrinter.GetNextJob skips it 535 fJob->Release(); 536 } else { 537 // job found 538 fJob->SetPrinter(this); 539 return true; 540 } 541 } 542 } while (fJob != NULL); 543 return false; 544 } 545 546 // --------------------------------------------------------------- 547 // PrintSpooledJob 548 // 549 // Loads the printer add-on and calls its take_job function with 550 // the spool file as argument. 551 // 552 // Parameters: 553 // spoolFile - the spool file. 554 // 555 // Returns: 556 // B_OK if successful. 557 // --------------------------------------------------------------- 558 status_t Printer::PrintSpooledJob(BFile* spoolFile) 559 { 560 take_job_func_t func; 561 image_id id; 562 status_t rc; 563 564 if ((rc=LoadPrinterAddon(id)) == B_OK) { 565 // Addon was loaded, so try and get the take_job symbol 566 if ((rc=get_image_symbol(id, "take_job", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 567 // This seems to be required for legacy? 568 // HP PCL3 add-on crashes without it! 569 BMessage params('_RRC'); 570 params.AddInt32("file", (int32)spoolFile); 571 params.AddInt32("printer", (int32)SpoolDir()); 572 // call the function and check its result 573 BMessage* result = (*func)(spoolFile, SpoolDir(), ¶ms); 574 575 if (result == NULL || result->what != 'okok') 576 rc = B_ERROR; 577 578 delete result; 579 } 580 581 ::unload_add_on(id); 582 } 583 584 return rc; 585 } 586 587 588 // --------------------------------------------------------------- 589 // PrintThread 590 // 591 // Loads the printer add-on and calls its take_job function with 592 // the spool file as argument. 593 // 594 // Parameters: 595 // job - the spool job. 596 // --------------------------------------------------------------- 597 void Printer::PrintThread(Job* job) { 598 // Wait until resource is available 599 fResource->Lock(); 600 bool failed = true; 601 // Can we continue? 602 if (!fAbort) { 603 BFile jobFile(&job->EntryRef(), B_READ_WRITE); 604 // Tell the printer to print the spooled job 605 if (jobFile.InitCheck() == B_OK && PrintSpooledJob(&jobFile) == B_OK) { 606 // Remove spool file if printing was successfull. 607 job->Remove(); failed = false; 608 } 609 } 610 // Set status of spooled job on error 611 if (failed) job->SetStatus(kFailed); 612 fResource->Unlock(); 613 job->Release(); 614 atomic_add(&fProcessing, -1); 615 Release(); 616 // Notify print_server to process next spooled job 617 be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB); 618 } 619 620 // --------------------------------------------------------------- 621 // print_thread 622 // 623 // Print thread entry, calls PrintThread with spool job. 624 // 625 // Parameters: 626 // data - spool job. 627 // 628 // Returns: 629 // 0 always. 630 // --------------------------------------------------------------- 631 status_t Printer::print_thread(void* data) { 632 Job* job = static_cast<Job*>(data); 633 job->GetPrinter()->PrintThread(job); 634 return 0; 635 } 636 637 // --------------------------------------------------------------- 638 // StartPrintThread 639 // 640 // Sets the status of the current spool job to "processing" and 641 // starts the print_thread. 642 // --------------------------------------------------------------- 643 void Printer::StartPrintThread() { 644 Acquire(); 645 thread_id tid = spawn_thread(print_thread, "print", B_NORMAL_PRIORITY, (void*)fJob); 646 if (tid > 0) { 647 fJob->SetStatus(kProcessing); 648 atomic_add(&fProcessing, 1); 649 resume_thread(tid); 650 } else { 651 fJob->Release(); Release(); 652 } 653 } 654 655