xref: /haiku/src/servers/print/Printer.cpp (revision 25a7b01d15612846f332751841da3579db313082)
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 
SpoolFolder(BLocker * locker,BLooper * looper,const BDirectory & spoolDir)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
Notify(Job * job,int kind)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*
Find(const BString & name)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*
Find(node_ref * node)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*
At(int32 idx)96 Printer::At(int32 idx)
97 {
98 	return sPrinters.ItemAt(idx);
99 }
100 
101 
102 void
Remove(Printer * printer)103 Printer::Remove(Printer* printer)
104 {
105 	sPrinters.RemoveItem(printer);
106 }
107 
108 
109 int32
CountPrinters()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 // ---------------------------------------------------------------
Printer(const BDirectory * node,Resource * res)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 
~Printer()149 Printer::~Printer()
150 {
151 	((PrintServerApp*)be_app)->NotifyPrinterDeletion(this);
152 }
153 
154 
155 void
MessageReceived(BMessage * msg)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
Remove()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
FindPathToDriver(const char * driverName,BPath * path)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
ConfigurePrinter(const char * driverName,const char * printerName)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
ConfigurePage(BMessage & settings)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
ConfigureJob(BMessage & settings)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
HandleSpooledJob()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
GetDefaultSettings(BMessage & settings)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
AbortPrintThread()323 Printer::AbortPrintThread()
324 {
325 	fAbort = true;
326 }
327 
328 
329 status_t
GetDriverName(BString * name)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
AddCurrentPrinter(BMessage & message)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
GetName(BString & name)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
ResetJobStatus()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
HasCurrentPrinter(BString & name)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
MoveJob(const BString & name)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
FindSpooledJob()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
PrintSpooledJob(const char * spoolFile)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
PrintThread(Job * job)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
print_thread(void * data)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
StartPrintThread()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