| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 
 | <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE chapter PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
<chapter id="devprinting">
<chapterinfo>
	<author>
		<firstname>Gerald</firstname><surname>Carter</surname>
	</author>
	<pubdate>October 2002</pubdate>
</chapterinfo>
<title>Samba Printing Internals</title>
<sect1>
<title>Abstract</title>
<para>
The purpose of this document is to provide some insight into
Samba's printing functionality and also to describe the semantics
of certain features of Windows client printing.
</para>
</sect1>
<sect1>
<title>
Printing Interface to Various Back ends
</title>
<para>
Samba uses a table of function pointers to seven functions.  The
function prototypes are defined in the <varname>printif</varname> structure declared
in <filename>printing.h</filename>.
</para>
<itemizedlist>
	<listitem><para>retrieve the contents of a print queue</para></listitem>
	<listitem><para>pause the print queue</para></listitem>
	<listitem><para>resume a paused print queue</para></listitem>
	<listitem><para>delete a job from the queue</para></listitem>
	<listitem><para>pause a job in the print queue</para></listitem>
	<listitem><para>result a paused print job in the queue</para></listitem>
	<listitem><para>submit a job to the print queue</para></listitem>
</itemizedlist>
<para>
Currently there are only two printing back end implementations
defined.
</para>
<itemizedlist>
	<listitem><para>a generic set of functions for working with standard UNIX
	printing subsystems</para></listitem>
	<listitem><para>a set of CUPS specific functions (this is only enabled if
	the CUPS libraries were located at compile time).</para></listitem>
</itemizedlist>
</sect1>
<sect1>
<title>
Print Queue TDB's
</title>
<para>
Samba provides periodic caching of the output from the "lpq command"
for performance reasons.  This cache time is configurable in seconds.
Obviously the longer the cache time the less often smbd will be
required to exec a copy of lpq.  However, the accuracy of the print
queue contents displayed to clients will be diminished as well.
</para>
<para>
The list of currently opened print queue TDB's can be found
be examining the list of tdb_print_db structures ( see print_db_head
in printing.c ). A queue TDB is opened using the wrapper function
printing.c:get_print_db_byname().  The function ensures that smbd
does not open more than MAX_PRINT_DBS_OPEN in an effort to prevent
a large print server from exhausting all available file descriptors.
If the number of open queue TDB's exceeds the MAX_PRINT_DBS_OPEN
limit, smbd falls back to a most recently used algorithm for maintaining
a list of open TDB's.
</para>
<para>
There are two ways in which a a print job can be entered into
a print queue's TDB.  The first is to submit the job from a Windows
client which will insert the job information directly into the TDB.
The second method is to have the print job picked up by executing the
"lpq command".
</para>
<para><programlisting>
/* included from printing.h */
struct printjob {
	pid_t pid; /* which process launched the job */
	int sysjob; /* the system (lp) job number */
	int fd; /* file descriptor of open file if open */
	time_t starttime; /* when the job started spooling */
	int status; /* the status of this job */
	size_t size; /* the size of the job so far */
	int page_count;	/* then number of pages so far */
	BOOL spooled; /* has it been sent to the spooler yet? */
	BOOL smbjob; /* set if the job is a SMB job */
	fstring filename; /* the filename used to spool the file */
	fstring jobname; /* the job name given to us by the client */
	fstring user; /* the user who started the job */
	fstring queuename; /* service number of printer for this job */
	NT_DEVICEMODE *nt_devmode;
};
</programlisting></para>
<para>
The current manifestation of the printjob structure contains a field
for the UNIX job id returned from the "lpq command" and a Windows job
ID (32-bit bounded by PRINT_MAX_JOBID).  When a print job is returned
by the "lpq command" that does not match an existing job in the queue's
TDB, a 32-bit job ID above the <*vance doesn't know what word is missing here*> is generating by adding UNIX_JOB_START to
the id reported by lpq.
</para>
<para>
In order to match a 32-bit Windows jobid onto a 16-bit lanman print job
id, smbd uses an in memory TDB to match the former to a number appropriate
for old lanman clients.
</para>
<para>
When updating a print queue, smbd will perform the following
steps ( refer to <filename>print.c:print_queue_update()</filename> ):
</para>
<orderedlist>
	<listitem><para>Check to see if another smbd is currently in 
	the process of updating the queue contents by checking the pid 
	stored in <constant>LOCK/<replaceable>printer_name</replaceable></constant>.  
	If so, then do not update the TDB.</para></listitem>
	
	<listitem><para>Lock the mutex entry in the TDB and store our own pid.
	Check that this succeeded, else fail.</para></listitem>
	<listitem><para>Store the updated time stamp for the new cache
	listing</para></listitem>
	<listitem><para>Retrieve the queue listing via "lpq command"</para></listitem>
	<listitem><para><programlisting>
	foreach job in the queue
     	{
		if the job is a UNIX job, create a new entry;
		if the job has a Windows based jobid, then
		{
			Lookup the record by the jobid;
			if the lookup failed, then
				treat it as a UNIX job;
			else
				update the job status only
		}
	}</programlisting></para></listitem>
	<listitem><para>Delete any jobs in the TDB that are not
	in the in the lpq listing</para></listitem>
	<listitem><para>Store the print queue status in the TDB</para></listitem>
	
	<listitem><para>update the cache time stamp again</para></listitem>
	
</orderedlist>
<para>
Note that it is the contents of this TDB that is returned to Windows
clients and not the actual listing from the "lpq command".
</para>
<para>
The NT_DEVICEMODE stored as part of the printjob structure is used to
store a pointer to a non-default DeviceMode associated with the print
job.  The pointer will be non-null when the client included a Device
Mode in the OpenPrinterEx() call and subsequently submitted a job for
printing on that same handle.  If the client did not include a Device
Mode in the OpenPrinterEx() request, the nt_devmode field is NULL
and the job has the printer's device mode associated with it by default.
</para>
<para>
Only non-default Device Mode are stored with print jobs in the print
queue TDB.  Otherwise, the Device Mode is obtained from the printer
object when the client issues a GetJob(level == 2) request.
</para>
</sect1>
<sect1>
<title>
ChangeID and Client Caching of Printer Information
</title>
<para>
[To be filled in later]
</para>
</sect1>
<sect1>
<title>
Windows NT/2K Printer Change Notify
</title>
<para>
When working with Windows NT+ clients, it is possible for a
print server to use RPC to send asynchronous change notification
events to clients for certain printer and print job attributes.
This can be useful when the client needs to know that a new
job has been added to the queue for a given printer or that the
driver for a printer has been changed.  Note that this is done
entirely orthogonal to cache updates based on a new ChangeID for
a printer object.
</para>
<para>
The basic set of RPC's used to implement change notification are
</para>
<itemizedlist>
	<listitem><para>RemoteFindFirstPrinterChangeNotifyEx ( RFFPCN )</para></listitem>
	<listitem><para>RemoteFindNextPrinterChangeNotifyEx ( RFNPCN )</para></listitem>
	<listitem><para>FindClosePrinterChangeNotify( FCPCN )</para></listitem>
	<listitem><para>ReplyOpenPrinter</para></listitem>
	<listitem><para>ReplyClosePrinter</para></listitem>
	<listitem><para>RouteRefreshPrinterChangeNotify ( RRPCN )</para></listitem>
</itemizedlist>
<para>
One additional RPC is available to a server, but is never used by the
Windows spooler service:
</para>
<itemizedlist>
	<listitem><para>RouteReplyPrinter()</para></listitem>
</itemizedlist>
<para>
The opnum for all of these RPC's are defined in include/rpc_spoolss.h
</para>
<para>
Windows NT print servers use a bizarre method of sending print
notification event to clients.  The process of registering a new change
notification handle is as follows.  The 'C' is for client and the
'S' is for server.  All error conditions have been eliminated.
</para>
<para><programlisting>
C:	Obtain handle to printer or to the printer
	server via the standard OpenPrinterEx() call.
S:	Respond with a valid handle to object
C:	Send a RFFPCN request with the previously obtained
	handle with either (a) set of flags for change events
	to monitor, or (b) a PRINTER_NOTIFY_OPTIONS structure
	containing the event information to monitor.  The windows
	spooler has only been observed to use (b).
S:	The <* another missing word*> opens a new TCP session to the client (thus requiring
	all print clients to be CIFS servers as well) and sends
	a ReplyOpenPrinter() request to the client.
C:	The client responds with a printer handle that can be used to
	send event notification messages.
S:	The server replies success to the RFFPCN request.
C:	The windows spooler follows the RFFPCN with a RFNPCN
	request to fetch the current values of all monitored
	attributes.
S:	The server replies with an array SPOOL_NOTIFY_INFO_DATA
	structures (contained in a SPOOL_NOTIFY_INFO structure).
C:	If the change notification handle is ever released by the
	client via a FCPCN request, the server sends a ReplyClosePrinter()
	request back to the client first.  However a request of this
	nature from the client is often an indication that the previous
	notification event was not marshalled correctly by the server
	or a piece of data was wrong.
S:	The server closes the internal change notification handle
	(POLICY_HND) and does not send any further change notification
	events to the client for that printer or job.
</programlisting></para>
<para>
The current list of notification events supported by Samba can be
found by examining the internal tables in srv_spoolss_nt.c
</para>
<itemizedlist>
	<listitem><para>printer_notify_table[]</para></listitem>
	<listitem><para>job_notify_table[]</para></listitem>
</itemizedlist>
<para>
When an event occurs that could be monitored, smbd sends a message
to itself about the change.  The list of events to be transmitted
are queued by the smbd process sending the message to prevent an
overload of TDB usage and the internal message is sent during smbd's
idle loop (refer to printing/notify.c and the functions
send_spoolss_notify2_msg() and print_notify_send_messages() ).
</para>
<para>
The decision of whether or not the change is to be sent to connected
clients is made by the routine which actually sends the notification.
( refer to srv_spoolss_nt.c:recieve_notify2_message() ).
</para>
<para>
Because it possible to receive a listing of multiple changes for
multiple printers, the notification events must be split into
categories by the printer name.  This makes it possible to group
multiple change events to be sent in a single RPC according to the
printer handle obtained via a ReplyOpenPrinter().
</para>
<para>
The actual change notification is performed using the RRPCN request
RPC.  This packet contains
</para>
<itemizedlist>
	
<listitem><para>the printer handle registered with the
client's spooler on which the change occurred</para></listitem>
<listitem><para>The change_low value which was sent as part
of the last RFNPCN request from the client</para></listitem>
<listitem><para>The SPOOL_NOTIFY_INFO container with the event
information</para></listitem>
</itemizedlist>
<para>
A <varname>SPOOL_NOTIFY_INFO</varname> contains:
</para>
<itemizedlist>
<listitem><para>the version and flags field are predefined
and should not be changed</para></listitem>
<listitem><para>The count field is the number of entries
in the SPOOL_NOTIFY_INFO_DATA array</para></listitem>
</itemizedlist>
<para>
The <varname>SPOOL_NOTIFY_INFO_DATA</varname> entries contain:
</para>
<itemizedlist>
<listitem><para>The type defines whether or not this event
is for a printer or a print job</para></listitem>
<listitem><para>The field is the flag identifying the event</para></listitem>
<listitem><para>the notify_data union contains the new valuie of the
attribute</para></listitem>
<listitem><para>The enc_type defines the size of the structure for marshalling
and unmarshalling</para></listitem>
<listitem><para>(a) the id must be 0 for a printer event on a printer handle.
(b) the id must be the job id for an event on a printer job
(c) the id must be the matching number of the printer index used
in the response packet to the RFNPCN when using a print server
handle for notification.  Samba currently uses the snum of
the printer for this which can break if the list of services
has been modified since the notification handle was registered.</para></listitem>
<listitem><para>The size is either (a) the string length in UNICODE for strings,
(b) the size in bytes of the security descriptor, or (c) 0 for
data values.</para></listitem>
</itemizedlist>
</sect1>
</chapter>
 |