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 171 Printer::~Printer() 172 { 173 ((PrintServerApp*)be_app)->NotifyPrinterDeletion(this); 174 } 175 176 177 // Remove printer spooler directory 178 status_t Printer::Remove() 179 { 180 status_t rc = B_OK; 181 BPath path; 182 183 if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY, &path)) == B_OK) { 184 path.Append(Name()); 185 rc = rmdir(path.Path()); 186 } 187 188 return rc; 189 } 190 191 // --------------------------------------------------------------- 192 // ConfigurePrinter 193 // 194 // Handles calling the printer addon's add_printer function. 195 // 196 // Parameters: 197 // none. 198 // 199 // Returns: 200 // B_OK if successful or errorcode otherwise. 201 // --------------------------------------------------------------- 202 status_t Printer::ConfigurePrinter() 203 { 204 image_id id; 205 status_t rc; 206 207 if ((rc=LoadPrinterAddon(id)) == B_OK) { 208 // Addon was loaded, so try and get the add_printer symbol 209 add_printer_func_t func; 210 211 if (get_image_symbol(id, "add_printer", B_SYMBOL_TYPE_TEXT, (void**)&func) == B_OK) { 212 // call the function and check its result 213 rc = ((*func)(Name()) == NULL) ? B_ERROR : B_OK; 214 } 215 216 ::unload_add_on(id); 217 } 218 219 return rc; 220 } 221 222 // --------------------------------------------------------------- 223 // ConfigurePage 224 // 225 // Handles calling the printer addon's config_page function. 226 // 227 // Parameters: 228 // settings - Page settings to display. The contents of this 229 // message will be replaced with the new settings 230 // if the function returns success. 231 // 232 // Returns: 233 // B_OK if successful or errorcode otherwise. 234 // --------------------------------------------------------------- 235 status_t Printer::ConfigurePage(BMessage& settings) 236 { 237 image_id id; 238 status_t rc; 239 240 if ((rc=LoadPrinterAddon(id)) == B_OK) { 241 // Addon was loaded, so try and get the config_page symbol 242 config_func_t func; 243 244 if ((rc=get_image_symbol(id, "config_page", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 245 // call the function and check its result 246 BMessage* new_settings = (*func)(SpoolDir(), &settings); 247 if (new_settings != NULL && new_settings->what != 'baad') { 248 settings = *new_settings; 249 AddCurrentPrinter(&settings); 250 } else 251 rc = B_ERROR; 252 delete new_settings; 253 } 254 255 ::unload_add_on(id); 256 } 257 258 return rc; 259 } 260 261 // --------------------------------------------------------------- 262 // ConfigureJob 263 // 264 // Handles calling the printer addon's config_job function. 265 // 266 // Parameters: 267 // settings - Job settings to display. The contents of this 268 // message will be replaced with the new settings 269 // if the function returns success. 270 // 271 // Returns: 272 // B_OK if successful or errorcode otherwise. 273 // --------------------------------------------------------------- 274 status_t Printer::ConfigureJob(BMessage& settings) 275 { 276 image_id id; 277 status_t rc; 278 279 if ((rc=LoadPrinterAddon(id)) == B_OK) { 280 // Addon was loaded, so try and get the config_job symbol 281 config_func_t func; 282 283 if ((rc=get_image_symbol(id, "config_job", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 284 // call the function and check its result 285 BMessage* new_settings = (*func)(SpoolDir(), &settings); 286 if ((new_settings != NULL) && (new_settings->what != 'baad')) { 287 settings = *new_settings; 288 AddCurrentPrinter(&settings); 289 } else 290 rc = B_ERROR; 291 delete new_settings; 292 } 293 294 ::unload_add_on(id); 295 } 296 297 return rc; 298 } 299 300 301 // --------------------------------------------------------------- 302 // HandleSpooledJobs 303 // 304 // Print spooled jobs in a new thread. 305 // --------------------------------------------------------------- 306 void Printer::HandleSpooledJob() { 307 BAutolock lock(gLock); 308 if (lock.IsLocked() && (!fSinglePrintThread || fProcessing == 0) && FindSpooledJob()) { 309 StartPrintThread(); 310 } 311 } 312 313 314 // --------------------------------------------------------------- 315 // GetDefaultSettings 316 // 317 // Retrieve the default configuration message from printer add-on 318 // 319 // Parameters: 320 // settings, output paramter. 321 // 322 // Returns: 323 // B_OK if successful or errorcode otherwise. 324 // --------------------------------------------------------------- 325 status_t Printer::GetDefaultSettings(BMessage& settings) { 326 image_id id; 327 status_t rc; 328 329 if ((rc=LoadPrinterAddon(id)) == B_OK) { 330 // Addon was loaded, so try and get the default_settings symbol 331 default_settings_t func; 332 333 if ((rc=get_image_symbol(id, "default_settings", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 334 // call the function and check its result 335 BMessage* new_settings = (*func)(SpoolDir()); 336 if (new_settings) { 337 settings = *new_settings; 338 AddCurrentPrinter(&settings); 339 } else { 340 rc = B_ERROR; 341 } 342 delete new_settings; 343 } 344 345 ::unload_add_on(id); 346 } 347 348 return rc; 349 } 350 351 352 void Printer::AbortPrintThread() { 353 fAbort = true; 354 } 355 356 // --------------------------------------------------------------- 357 // LoadPrinterAddon 358 // 359 // Try to load the printer addon into memory. 360 // 361 // Parameters: 362 // id - image_id set to the image id of the loaded addon. 363 // 364 // Returns: 365 // B_OK if successful or errorcode otherwise. 366 // --------------------------------------------------------------- 367 status_t Printer::LoadPrinterAddon(image_id& id) 368 { 369 BString drName; 370 status_t rc; 371 BPath path; 372 373 if ((rc=SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_DRV_NAME, &drName)) == B_OK) { 374 // try to locate the driver 375 if ((rc=::TestForAddonExistence(drName.String(), B_USER_ADDONS_DIRECTORY, "Print", path)) != B_OK) { 376 if ((rc=::TestForAddonExistence(drName.String(), B_COMMON_ADDONS_DIRECTORY, "Print", path)) != B_OK) { 377 rc = ::TestForAddonExistence(drName.String(), B_BEOS_ADDONS_DIRECTORY, "Print", path); 378 } 379 } 380 381 // If the driver was found 382 if (rc == B_OK) { 383 // If we cannot load the addon 384 if ((id=::load_add_on(path.Path())) < 0) 385 rc = id; 386 } 387 } 388 return rc; 389 } 390 391 void Printer::AddCurrentPrinter(BMessage* msg) 392 { 393 BString name; 394 GetName(name); 395 if (msg->HasString(PSRV_FIELD_CURRENT_PRINTER)) { 396 msg->ReplaceString(PSRV_FIELD_CURRENT_PRINTER, name.String()); 397 } else { 398 msg->AddString(PSRV_FIELD_CURRENT_PRINTER, name.String()); 399 } 400 } 401 402 void Printer::MessageReceived(BMessage* msg) 403 { 404 switch(msg->what) { 405 case B_GET_PROPERTY: 406 case B_SET_PROPERTY: 407 case B_CREATE_PROPERTY: 408 case B_DELETE_PROPERTY: 409 case B_COUNT_PROPERTIES: 410 case B_EXECUTE_PROPERTY: 411 HandleScriptingCommand(msg); 412 break; 413 414 default: 415 Inherited::MessageReceived(msg); 416 } 417 } 418 419 void Printer::GetName(BString& name) { 420 if (B_OK != SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name)) 421 name = "Unknown Printer"; 422 } 423 424 bool Printer::HasCurrentPrinter(BString& name) { 425 BMessage settings; 426 // read settings from spool file and get printer name 427 BFile jobFile(&fJob->EntryRef(), B_READ_WRITE); 428 return jobFile.InitCheck() == B_OK && 429 jobFile.Seek(sizeof(print_file_header), SEEK_SET) == sizeof(print_file_header) && 430 settings.Unflatten(&jobFile) == B_OK && 431 settings.FindString(PSRV_FIELD_CURRENT_PRINTER, &name) == B_OK; 432 } 433 434 bool Printer::MoveJob(const BString& name) { 435 BPath file(&fJob->EntryRef()); 436 BPath path; 437 file.GetParent(&path); 438 path.Append(".."); path.Append(name.String()); 439 BDirectory dir(path.Path()); 440 BEntry entry(&fJob->EntryRef()); 441 // try to move job file to proper directory 442 return entry.MoveTo(&dir) == B_OK; 443 } 444 445 bool Printer::FindSpooledJob() { 446 BString name2; 447 GetName(name2); 448 do { 449 fJob = fPrinter.GetNextJob(); 450 if (fJob) { 451 BString name; 452 if (HasCurrentPrinter(name) && name != name2 && MoveJob(name)) { 453 fJob->SetStatus(kUnknown, false); // so that fPrinter.GetNextJob skips it 454 fJob->Release(); 455 } else { 456 fJob->SetPrinter(this); 457 return true; 458 } 459 } 460 } while (fJob != NULL); 461 return false; 462 } 463 464 status_t Printer::PrintSpooledJob(BFile* spoolFile) 465 { 466 take_job_func_t func; 467 image_id id; 468 status_t rc; 469 470 if ((rc=LoadPrinterAddon(id)) == B_OK) { 471 // Addon was loaded, so try and get the take_job symbol 472 if ((rc=get_image_symbol(id, "take_job", B_SYMBOL_TYPE_TEXT, (void**)&func)) == B_OK) { 473 // This seems to be required for legacy? 474 // HP PCL3 add-on crashes without it! 475 BMessage params('_RRC'); 476 params.AddInt32("file", (int32)spoolFile); 477 params.AddInt32("printer", (int32)SpoolDir()); 478 // call the function and check its result 479 BMessage* result = (*func)(spoolFile, SpoolDir(), ¶ms); 480 481 if (result == NULL || result->what != 'okok') 482 rc = B_ERROR; 483 484 delete result; 485 } 486 487 ::unload_add_on(id); 488 } 489 490 return rc; 491 } 492 493 494 void Printer::PrintThread(Job* job) { 495 // Wait until resource is available 496 fResource->Lock(); 497 bool failed = true; 498 // Can we continue? 499 if (!fAbort) { 500 BFile jobFile(&job->EntryRef(), B_READ_WRITE); 501 // Tell the printer to print the spooled job 502 if (jobFile.InitCheck() == B_OK && PrintSpooledJob(&jobFile) == B_OK) { 503 // Remove spool file if printing was successfull. 504 job->Remove(); failed = false; 505 } 506 } 507 // Set status of spooled job on error 508 if (failed) job->SetStatus(kFailed); 509 fResource->Unlock(); 510 job->Release(); 511 atomic_add(&fProcessing, -1); 512 Release(); 513 // Notify print_server to process next spooled job 514 be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB); 515 } 516 517 status_t Printer::print_thread(void* data) { 518 Job* job = static_cast<Job*>(data); 519 job->GetPrinter()->PrintThread(job); 520 return 0; 521 } 522 523 void Printer::StartPrintThread() { 524 Acquire(); 525 thread_id tid = spawn_thread(print_thread, "print", B_NORMAL_PRIORITY, (void*)fJob); 526 if (tid > 0) { 527 fJob->SetStatus(kProcessing); 528 atomic_add(&fProcessing, 1); 529 resume_thread(tid); 530 } else { 531 fJob->Release(); Release(); 532 } 533 } 534 535