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