大判プリンタに対する印刷枚数を集計する CUPS サーバ

# 概要

情報系センターでは、プリンタの印刷枚数を集計・課金するという業務が多く発生します。しかし、大判プリンタに対応した集計ソフトウェアは、ほとんど存在しません。

また、CUPS サーバのログ `/var/log/cups/page_log` を調べると、CUPS サーバに印刷ジョブが投入された回数は分かるのですが、個々の印刷ジョブに含まれるページ数や部数が分からないので、印刷枚数は集計できません。

本稿では、既存の集計ソフトウェアが対応していないプリンタに対する印刷枚数を集計する CUPS サーバの設定手順を説明します。


# 設定手順

ホスト名が `ps00.example.net` であるプリンタが、ESC/P 形式のデータを 9100/tcp ポートで受け付けている時、このプリンタに対する印刷枚数を集計するための設定手順を例示します。

(1) プリンタを管理する CUPS サーバを用意します。

```shell-session
# apt install cups cups-client
```

(2) [cupsyslog](https://gist.github.com/tsuchm/78233756dcf17725ea42450b44da2bbc) を、CUPS サーバの `/usr/lib/cups/backend/` 配下にコピーします。

(3) lpadmin コマンドを使ってプリンタを定義します。lpadmin コマンドの使い方は以下の通りです。

```shell-session
# lpadmin -p プリンタ名 -v プリンタURI -E
```

ここでは、cupsyslog を経由して、プリンタジョブを `ps00.example.net` に送信するように指定したいので、以下のようなコマンド例になります。

```shell-session
# lpadmin -p ps00 -v cupsyslog://escp/socket://ps00.example.net:9100 -E
```

cupsyslog を使う場合のプリンタ URI は、以下のような構成になっています。

| バックエンド | デリミタ | カウンタ種別 | デリミタ | 実プリンタURI |
|-----------|-----|------|---|--------------------------------| 
| cupsyslog | :// | escp | / | socket://ps00.example.net:9100 |

このプリンタ URI は、cupsyslog に対して、

* データは ESC/P 形式であることを仮定して、印刷枚数を集計せよ
* データは、実プリンタURI `socket://ps00.example.net:9100` に転送せよ

ということを指定しています。

(4) 集計結果は、cupsyslog から syslog サーバに通知されます。記録先は syslogd の設定によって異なりますが、例えば `/var/log/messages` に以下のようなログが記録されるはずですので、適当なスクリプトを作成して集計して下さい。

```text
Aug 12 06:25:14 cups cupsyslog[1234]: PRINTED: user=testuser, num_copies=1, num_pages=5, printer=ps00, job="example", title="example", options="", file="example"
```

(5) CUPS のプリンタを Samba 経由で共有する設定をします。

```shell-session
# apt install samba
```
Samba の設定ファイルは、以下のような内容になります。AD サーバが既に動作している環境で、余計なことは一切行わずに、単にプリンタの共有だけを提供することを意図している設定です。

```conf:/etc/samba/smb.conf
# Global parameters
[global]
        workgroup = WORKGROUP
        realm = WORKGROUP.EXAMPLE.NET
        security = ads
        server string = CUPS server
        netbios name = cups

        local master = No
        wins support = No
        dns proxy = No
        name resolve order = lmhosts host wins bcast
        bind interfaces only = Yes
        interfaces = lo, eth0
        log file = /var/log/samba/%m.log
        max log size = 1000
        log level = 3
        syslog = 2
        panic action = /usr/share/samba/panic-action %d

        printer admin = @printeradministrators

[printers]
        comment = All Printers
        browseable = No
        path = /var/spool/samba
        printable = Yes
        read only = Yes
        write list = @printerusers
        guest ok = No
        create mask = 0700
```

# 対応プリンタ

cupsyslog には、筆者の必要によって、以下の4種類のカウンタが定義されています。

|カウンタ種別|対応プリンタ|
|----------|-----------------------|
|escp|ESC/P 言語をサポートしたプリンタ|
|hpgl|HP-GL 言語をサポートしたプリンタ|
|mjc|EPSON PM-8000C|
|null|実際には集計しない。常に1ページと通知するダミープリンタ|

対応プリンタを増やすには、なんらかの方法で、対象プリンタに送られるデータのコピーが必要です。

1. 最初に、2ページの文書を用意します。そして、1ページだけを印刷した場合のデータと、2ページともに印刷した場合のデータを比較します。多くの場合、この比較で、「改ページ」を指定するプリンタコマンド文字列が判明します。
2. 次に、この文書を、1部だけ印刷した場合のデータと、2部印刷した場合のデータを比較します。多くの場合、この比較で、「部数」を指定するプリンタコマンド文字列が判明します。

例えば、cupsyslog の hpgl_counter() は、

* 改ページコマンド文字列 `PG;` の出現回数
* 部数コマンド文字列 `RP###;` （### の部分は実際には数字）の出現回数

を集計する関数となっています。

このように、改ページコマンド文字列と部数コマンド文字列を調査して関数を作成し、作成した関数を `%COUNTER` に追加すれば、対応プリンタを増やすことができます。

# 制限

実は、プリンタで出力された枚数を正確に集計するのは、見かけほど簡単な処理ではありません。例えば、端末から CUPS サーバを経由してプリンタに印刷データが送信された後、プリンタで取消ボタンが押された場合、印刷枚数として計上しない処理が必要になります。が、そのように正確に集計するには、プリンタと連携して動作するシステムが必要となり、必然的に大変高価なシステムとなります。本稿で紹介する方法は、あくまでも CUPS サーバ上で集計する簡易な方法になります。