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