第15章 Samba印刷機能の詳細

Gerald Carter

October 2002

目次

概要
種々のバックエンドに対する印刷インタフェース
印刷キューTDB
ChangeIDとプリンター情報のキャッシング
Windows NT/2K におけるPrinter Change Notify

概要

この文書の目的は、いくつかのSambaの印刷機能についての概要を提供することと、 Windowsクライアントの印刷の特定の機能についての説明を行うことである。

種々のバックエンドに対する印刷インタフェース

Sambaは7つの機能に対する関数ポインターテーブルを使う。関数のプロトタイプは printing.h中で記述されるprintif構造体中で 定義されている。

  • retrieve the contents of a print queue

  • pause the print queue

  • resume a paused print queue

  • delete a job from the queue

  • pause a job in the print queue

  • result a paused print job in the queue

  • submit a job to the print queue

現在、2つの印刷バックエンドの実装のみが定義されている。

  • 標準UNIX印刷サブシステムと共に動作するための汎用的な機能セット

  • CUPS固有の機能セット(これはCUPSライブラリがコンパイル時に存在 する場合にのみ有効となる)。

印刷キューTDB

Sambaはパフォーマンス向上のために、"lpq command"からの出力を定期的にキャッシュする 機能を提供する。このキャッシュ時間は秒単位で設定可能である。明らかに、キャッシュ時間が より長いと、それだけsmbdはlpqへのコピーを行う回数が減る。しかし、クライアントに見せる 印刷キューの内容の正確さは、減少するだろう。

現在オープンしている印刷キューTDBのリストは、tdb_print_db structuresのリストを 調べることによって見つけることが出来る(printing.c中のprint_db_head参照)。1つのキュー TDBはラッパー関数printing.c:get_print_db_byname()を使うことによってオープンされる。 関数は、すべての有効なファイルディスクリプタを使い果たしてしまうことから、大きな 印刷サーバーを防ぐための効果がある、MAX_PRINT_DBS_OPENよりも数多く開かない事をsmbdに 行わせる。もしもキューTDBのオープン数がMAX_PRINT_DBS_OPENの制限を超過した場合、 smbdはオープンTDBのリストを管理するためMRU(most recently used)アルゴリズムを使う。

印刷ジョブを印刷キューTDB中に投入できるための2つの方法がある。最初のものはTDB中に ジョブ情報を直接挿入するWindowsクライアントからジョブを投入する方法である。2番目の 方法は存在している"lpq command"を実行することで、印刷ジョブをピックアップする方法である。

/* 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;
};

printjob 構造体の現在の状態には、"lpq command"から戻されるUNIXジョブIDと、 Windows ジョブID(PRINT_MAX_JOBIDによって32ビットごとに区切られる) のためのフィールドが含まれている。印刷ジョブが、キューTDB中に存在するジョブに 一致しない"lpq command"によって戻された場合、上記の32ビットジョブID <*vance doesn't know what word is missing here*>がlpqによって報告される ID用にUNIX_JOB_STARTを追加して生成される。

32ビットWindowsジョブIDを16ビットlanman印刷ジョブIDに一致させるために、smbdは 古いlanmanクライアントのために、前半部分の適切な数値を一致させるためのメモリTDBを使う。

印刷キューを更新する時、smbdは以下の手順を実行する (print.c:print_queue_update()を参照):

  1. もしも、他のsmbdが現在キューの内容を更新中かどうかを、 LOCK/printer_nameに 格納されているpidをチェックすることで調べる。もしもそうであれば、 TDBの更新をしない。

  2. TDB中のmutexエントリをロックし、今のプロセス固有のpidを格納する。 それがうまくいったかを調べ、そうでなければ失敗する。

  3. 新しいキャッシュ内容の表示のために、更新したタイムスタンプを 格納する。

  4. "lpq command"経由でキューの一覧を検索する。

  5. 	foreach job in the queue
         	{
    		ジョブがUNIXジョブならば、新しいエントリを作成する;
    		ジョブがWindowsベースのジョブIDを持つならば、
    		{
    			ジョブIDでレコードを検索する;
    			もしも検索が失敗したら
    				それをUNIXジョブとして扱う;
    			そうでなければ
    				ジョブステータスのみを更新する
    		}
    	}
  6. lpqの一覧中に無い、TDB中のジョブを削除する。

  7. TDB中に印刷キューステータスを格納する。

  8. 再度キャッシュされたタイムスタンプを更新する。

これは、Windowsクライアントに返されるTDBの内容であり、"lpq command"からの実際の一覧とは 違うことに注意。

printjob構造体の一部として格納されるNT_DEVICEMODEは、印刷ジョブに関連づけられる 非標準のDeviceModeへのポインターを格納するのに使われる。ポインターは、クライアントが OpenPrinterEx()呼び出し中でDevice Modeを含める時にnull以外になり、その同じハンドルで 印刷のために引き続いてジョブを投入する。もしもクライアントがOpenPrinterEx()要求に 対してDevice Modeを含めないのであれば、nt_devmodeフィールドはNULLであり、ジョブは 既定値で、それに関連づけられるプリンターのデバイスモードを持つ。

非標準のDevice Modeのみが印刷キューTDB中にプリントジョブと一緒に格納される。それ以外は、 Device Modeは、クライアントがGetJob(level == 2)要求を発行した時、プリンターオブジェクト から得られたものである。

ChangeIDとプリンター情報のキャッシング

[後日作成]

Windows NT/2K におけるPrinter Change Notify

Windows NT+クライアントで動作している場合、プリンターサーバーに対してRPCを使って、 クライアントに、特定のプリンターと印刷ジョブ属性の、非同期な変更通知イベント を送ることが出来る。これは、クライアントが指定したプリンターに対して新しいジョブが キューに追加されたことか、プリンターのドライバーが変更されたかを知る必要がある時に 便利である。これは、プリンターオブジェクトのなめの新しいChangeID上をベースとした キャッシュ更新とは完全に無関係に行われることに注意。

変更通知を実装するために使われる基本的なRPCの使用範囲は以下の通り

  • RemoteFindFirstPrinterChangeNotifyEx ( RFFPCN )

  • RemoteFindNextPrinterChangeNotifyEx ( RFNPCN )

  • FindClosePrinterChangeNotify( FCPCN )

  • ReplyOpenPrinter

  • ReplyClosePrinter

  • RouteRefreshPrinterChangeNotify ( RRPCN )

1つの追加RPCがサーバーに対して有効であるが、それはWindows spoolerサービスでは決して 使われない:

  • RouteReplyPrinter()

それらすべてのRFC用のopnumはinclude/rpc_spoolss.hで定義されている。

Windows NT印刷サーバーはクライアントに印刷通知イベントを送る変わった方法を使っている。 新しい変更通知ハンドルの登録プロセスは、以下のようになる。'C'はクライアントで、 'S'はサーバーである。すべてのエラー状態は省略されている。

C:	標準OpenPrinterEx()呼び出しを経由して、プリンターかプリンターサーバーへの
	ハンドルを得る。
S:	オブジェクトへの有効なハンドルを返す。

C:	(a)モニターへの変更イベントのための1まとまりのフラグか、(b)モニターへの
	イベント情報を含むPRINTER_NOTIFY_OPTIONS構造体のどちらかと共に、
	以前に入手したハンドルと共にRFFPCN要求を送る。Windows spoolerは
	(b)の使用のみを観察している。
S:	<*他の間違った単語*>はクライアントに対する新しいTCPセッションを開き
	(そのため、すべての印刷クライアントはCIFSサーバーであるように要求する)、
	クライアントに対して、ReplyOpenPrinter()要求を送信する。
C:	クライアントは、イベント通知メッセージを送るために使われる事が出来る、
	プリンターハンドルを返す。
S:	サーバーはRFFPCN要求に対して成功を返す。

C:	Windows spoolerは、すべての、モニターしている属性の現在値を取得するために、
	RFNPCNを使うRFFPCNをフォローする。
S:	サーバーはSPOOL_NOTIFY_INFO_DATA構造体(SPOOL_NOTIFY_INFO構造体を含む)を
	返す。

C:	もしも、変更通知ハンドルがFCPCN要求経由でクライアントにより常に開放されるならば、
	サーバーは最初にクライアントに対してReplyClosePrinter()要求を返す。しかし、
	クライアントからのこの性質の要求は、しばしば以前の通知イベントがサーバーによって、
	正しく整理されなかったか、データの一部分が間違っていたかを示すものである。
S:	サーバーは内部の変更通知ハンドル(POLICY_HND)をクローズし、そのプリンターかジョブに
	対する変更通知イベントは、その後クライアントに送らないようになる。

Sambaによってサポートされている、現在の通知イベントのリストは、srv_spoolss_nt.c中の 内部テーブルを捜すことで見つけることが出来る。

  • printer_notify_table[]

  • job_notify_table[]

モニターされているイベントが発生した時、smbdは変更についてそれ自身にメッセージを送る。 送信されるイベントのリストは、目セージの送信によって、TDBの利用状態が高負荷になる ことを防ぐためにsmbdプロセスによってキューされ、内部メッセージは、smbdがアイドル 状態の時に(printing/notify.cとsend_spoolss_notify2_msg()関数と print_notify_send_messages()関数を参照)送信される。

変更状態を、接続されているクライアントに送るかどうかの決定は、実際に通知を送る ルーチンによって行われる(srv_spoolss_nt.c:recieve_notify2_message()を参照)。

複数のプリンターの複数の変更の一覧を受け取ることが可能という理由で、通知イベントは プリンター名の区分けによって分離される必要がある。これにより、ReplyOpenPrinter()経由で 得られるプリンターハンドルによる、単一のRPCで、複数の変更イベントを送れるようになる。

実際の変更通知はRRPCN要求RPCを使って実行される。このパケットは以下を含む

  • 変更が起こったところのクライアントのスプーラとして登録された プリンターハンドル

  • クライアントからの最後のRFNPCN要求の一部として送られる 変更の生の値(change_low value)

  • イベント情報を持つSPOOL_NOTIFY_INFOコンテナー

SPOOL_NOTIFY_INFOは以下を含む:

  • バージョンとフラグ欄はあらかじめ定義され、変更すべきでない

  • count欄はSPOOL_NOTIFY_INFO_DATA配列内のエントリ数である。

SPOOL_NOTIFY_INFO_DATAは以下を含む:

  • typeは、このイベントがプリンターからか、あるいは印刷ジョブからかを 定義する

  • fieldは、イベントを識別するフラグである

  • notify_data unionには新しい属性値が含まれている。

  • enc_typeは、整列しているかいないかの構造体の大きさを定義する。

  • (a) プリンターハンドル上のプリンターイベントに対しては、idは0でなければならない。 (b)idは、プリンタージョブ上のイベントのジョブidでなければならない。 (c)idは、通知のためのプリントサーバーハンドルを使う時にRFNPCNに対する応答パケット中で 使われるプリンターインデックスの番号と一致しなければならない。Sambaは現在、 もしも通知ハンドルが登録されてから、サービスのリストが、変更された場合、変わっているかも しれないプリンターのsnumを使う。

  • サイズは(a)UNICODEでの文字列の長さか、(b)セキュリティディスクリプタの バイト値か(c)データ値の場合は0である。