I needed to FAX a prescription to my pharmacist. Formerly on our home office machine we had Windows XP Professional, which has FAX software, but we upgraded to Windows Vista Home Premium, which I discovered does not have it. One strategy would have been to upgrade to Vista Business or Ultimate, but we decided that that was unreasonable.
Since I'm a Linux geek, I of course proposed doing the job on our home server.
One should approach hardware selection in this order:
What do you want the hardware to do for you? Send and receive FAX.
What software will you run to do this? HylaFAX, efax or mgetty on Linux.
What hardware features does the software need? This is going to be running on the home server, which is all USB (not PCI), so it has to be a USB FAX modem and must have a Linux driver. We send few FAXes, so it is not important to have the maximum speed, nor high-volume features such as class 2 capability. We expect to never use the data (dialup) capabilities which undoubtedly will come with the modem. Voice capabilities are intriguing but are not part of this project.
Which hardware vendor's offering provides these capabilities? In a search on Google, the only USB FAX modem that I discovered that claimed to work with Linux was this one:
compatible withv2.0 (i.e. Full Speed device)
According to Conexant's hype about the CX93010 chipset, it should be quite popular among modem designers, and it is very likely that other USB, PCI and DB-9 modems include it, but their sales information fails to identify the chipset. This document declares (correctly) that the chipset works with Linux. It identifies ITU V.253 as the standard which the answering machine commands follow. The chipset can recognize when the remote partner hangs up or if a local extension is picked up.
You just plug in a USB device and it works, right? Yeah, sure. These messages were seen in syslog upon hotplug:
But no serial device appeared. It turns out that you need to do this: (Credit to this howto from the Irish Linux Users' Group.)
modprobe usbserial vendor=0x0572 product=0x1321
Once that was done, these devices were created by udev:
Device | Major | Minor | Function |
---|---|---|---|
/dev/bus/usb/001/017 | 189 | 16 | USB Device |
/dev/usbdev1.17_ep00 | 254 | 19 | ? |
/dev/usbdev1.17_ep02 | 254 | 22 | CDC Data |
/dev/usbdev1.17_ep81 | 254 | 20 | Abstract Modem |
/dev/usbdev1.17_ep82 | 254 | 21 | CDC Data |
/dev/ttyUSB0 | 188 | 0 | Serial Line |
The modem's USB ID (vendor and product codes) do not appear in
/lib/modules/$version/modules.usbmap or anywhere else, so only limited hotplug
support is possible out of the box
. I added two automation scripts
to make the modem function after hotplugging (particularly,
when coldplugged after a reboot):
You need to load the usbserial driver specifying the vendor and product codes. Put this in a file in /etc/modprobe.d. The given command lines will be used when cdc_acm, the abstract modem driver, is to be installed or removed. But things get more complicated if two different brands of modem are present on the same system.
install cdc_acm /sbin/modprobe --ignore-install cdc_acm ; /sbin/modprobe usbserial vendor=0x0572 product=0x1321 remove cdc_acm /sbin/modprobe -r usbserial ; /sbin/modprobe -r --ignore-install cdc_acm
With this done and usbserial loaded, I was able to use Minicom (described at the end of the document) to dial the modem and make a data connection to a modem at work, as a test.
I'm looking ahead to using a pair of these modems, one for each of our two phone lines, as a telephone answering machine. The FAX configuration needs to refer to the modem(s) explicitly, but USB devices tend to appear during hot or coldplug in a random order and so to have random minor device numbers and names. An invariant symbolic link can help this, created by a udev rule. First locate the correct device (here it will be /dev/ttyUSB0) and do:
udevinfo -q path -n ttyUSB0It prints a path relative to /sys, represented by $path in the next command line:
udevinfo -p $path -aIt prints quite a lot of material (you might pipe it into
less). Pick out attributes that uniquely identify the device and copy them into a udev rule. You can pick up to five attributes, using those for the device itself (first stanza) and any one ancestor directory. The rules belong in /etc/udev/rules.d and lexically earlier filenames have priority; my filename is 25-jimc.rules. My rule for the FAX modem is (all on one line):
ATTRS{idVendor}=="0572" ATTRS{idProduct}=="1321" ATTRS{serial}=="24680246" SUBSYSTEM=="tty" SYMLINK="faxmodem0"Since the product code is in an ancestor device you need SUBSYSTEM=="tty" so the symlink will be made to the correct device; otherwise the link would be made to usbdev1.19_ep81.
As one way to send a FAX I'm going to configure a network printer. Since I use CUPS for printing, I'll be using fax4CUPS (see below). This software supports four FAX server programs, from which I need to pick one:
This is for ISDN. Useless for us.
Quoting from their page: EFAX is a nice simple program for
single user systems.
It's attractive, but we want a Windows
client and it is not intended for network use. If it's sufficient to
print outgoing FAXes to a network printer, and for incoming FAXes to
be mailed to a secretary or left as files on the server, then
you may like efax.
Simple, available, but sparse. Included in SuSE. Same comments apply as to efax. Note: the HylaFAX documentation mentions coordination with mgetty. That is not the one that comes with this package.
This is the benchmark FAX server software for Linux. Included in SuSE. All the good Windows clients talk to HylaFAX. This is the one we will install.
Hylafax has good online
documentation which you should read. I followed the instructions for
Basic Server Configuration
. Here are some notes on steps which
were not obvious.
When you first install HylaFAX you need to execute faxsetup
(no command line arguments). I captured the output with script
and
saved it in ~fax/etc for future reference.
The Hylafax configuration script says that it wants mgetty, vgetty and
egetty. SuSE package mgetty
includes the mgetty and vgetty programs.
However, someone in 1996 warns that the vgetty that comes with mgetty doesn't
work properly with HylaFAX. As for egetty, nobody knows what it is or where to
find it. People just ignore the warning messages for the missing getty
variants and continue with configuration.
Faxsetup says: Warning: /usr/share/ghostscript/Resource does not exist or is not a directory!
(PostScript font metric files are here.)
We have /usr/share/ghostscript/8.15/Resource. To fix:
ln -s 8.15/Resource /usr/share/ghostscript/Resource (needs bug report).
At this point I killed faxsetup and restarted it from the beginning.
It asks: Do you want to run faxaddmodem to configure a modem [yes]? Serial port that modem is connected to []? ttyUSB0. It wants the basename of the device; it will provide /dev/ itself.
When doing modem configuration, the script failed to find the fuser command (needs bug report). It's in /bin/fuser. Lack of fuser did not seem to spoil the setup; most likely it was checking whether a lock file was locked, which it wasn't.
At one step the script points you to man page config(5F), which for SuSE really is hylafax-config.
Phone number of fax modem [+1 310 555-1212]? It wants your phone number, which it can put in the tagline. Many locales, including the USA, require that all FAXes identify the sender by showing at least the sending phone number on each page. HylaFAX (and many store-bought FAX machines) comply by putting a text line at the top of the page, called the tagline.
Rings to wait before answering [1]? 6. This decision is important. Our answering machine is set to pick up after 4 rings. When we expect a FAX we will unplug it, whereupon HylaFAX will answer after 6 rings. An alternative is to set this to 0, meaning don't answer automatically. Then the operator must execute "faxanswer" which signals it to answer the call.
A much more high-tech solution would be to use the modem's voice capability and turn the server into an answering machine. This modem can switch automatically between voice, data and FAX depending on the carrier that the caller transmits. Not every modem can do that.
Max number of pages to accept in a received facsimile [25]? This is the default. While a safety limit is a good idea, I wonder if we're ever going to need to accept larger documents by FAX.
This modem can do class 1 and 1.0. Configured for class 1. The HylaFAX dccumentation suggests not using class 2 and friends (advanced features) because the modem's firmware is often buggy, and HylaFAX is perfectly capable of coordinating transmission with the remote site by itself.
It says: Don't forget to run faxmodem(8C) (if you have a send-only environment) or configure init to run faxgetty on ttyUSB0. I declined the script's offer to run faxmodem; I'll use faxgetty. In /etc/inittab, the ID field (first one) must be unique 1-4 bytes. It is traditionally the name of the tty minus "tty", e.g. /dev/ttyS0 -> S0, but this is only tradition and not actually enforced. I used FAX0. Here is the resulting inittab line:
FAX0:35:respawn:/usr/lib/fax/faxgetty faxmodem0
They recommend to use cron to run faxqclean hourly, and to run faxcron daily. I edited /etc/cron.daily/suse.de-faxcron to direct its output to /var/spool/fax/log/faxcron.out, to rotate this file, and to also run faxqclean (daily). We don't send enough FAXes to justify frequent queue cleanups.
SuSE's HylaFAX was not compiled with PAM authentication! Hiss, boo! (Needs bug report.) This is going to make individual authentication rather difficult. We will use hostbased authentication instead. Of course when you submit an outgoing FAX via a pseudo-printer, the protocol does not include any way to authenticate stronger than the honor system. To activate hostbased authentication you need to edit ~fax/etc/hosts.hfaxd listing the IP addresses (hostnames may or may not also work) of the authorized hosts. Wildcards are allowed. This is the line I added, to authorize any user on my internal subnet:
192.9.200.*
What is being authorized here? Sending a FAX using the HylaFAX protocol (versus CUPS and the FAX printer), which is not a big deal, but also reading received FAXes. In the corporate world it could be very important to restrict received FAXes to appropriate personnel.
For testing, or in gateway scripts, you can use the sendfax command. See its man page for usage instructions. Incoming FAXes are stored in ~fax/recvq; client programs can retrieve them by contacting the HylaFAX daemon hfaxd on port 4559.
It all seems to be working, including through reboots. It sends faxes originating on the local machine via the sendfax command itself, or by fax4CUPS (printing on the FAX printer).
Recipient Format: The main information in
the recipient
field is the phone number of the remote FAX machine, but
the full name of the recipient can be prepended, separated with an at sign,
like this (from the sendfax man page): Sam Leffler@+1.415.555.1212
.
The full name is removed and inserted in the cover page.
Hyphens, dots or blanks can be added to the phone number for readability; they
are ignored.
Cover Pages: By default, HylaFAX creates a cover page for each transmitted FAX. However, some packages, specifically fax4CUPS, suppress this cover page by giving the -n option to sendfax. You will need to hack the backend script to turn it back on. The cover page can be customized; it is left as an exercise to the reader to locate the default template file, view it (use faxcover, which has a man page), and substitute a custom template (with SuSE, see /usr/share/doc/packages/hylafax/latex-cover-1.04/README).
CUPS is one of the currently more popular printing subsystems for Linux, using the IPP protocol which Windows also uses. Fax4CUPS is a CUPS backend that sends the printed job as a FAX. I downloaded and installed the RPM package (20 Kb). Here are some notes on the installation.
Its post-install scriptlet got an error (nonfatal): it wants to execute /etc/rc.d/init.d/cups, which is what they do in Red Hat, but not in UnitedLinux (SuSE). It has variants (discussed in a previous section) for these FAX systems: capisuite, efax, mgetty-fax and HylaFAX.
The URI used in CUPS for HylaFAX is hylafax:/local
. Using the CUPS
administrative interface, you set up a new printer to use this URI.
Unfortunately, with cups-1.2 you don't get the opportunity to specify the URI;
the only choices are efax on the first or second serial port. Finish creating
the printer, then stop CUPS, and edit /etc/cups/printers.conf. For your
printer, find DeviceURI and change to hylafax:local. Start CUPS again.
Upon installation you need to create /etc/sysconfig/fax with these variables. Except the provided backend script /usr/lib/cups/backend/hylafax does not read the file! You need to either set the defaults in the script and/or hack the script to read the sysconfig file as advertised by the documentation.
You also need to add to /etc/sudoers a line so that
sudo -u $JOB_OWNER /usr/bin/sendfax
will be allowed, like this one,
assuming that CUPS runs as the user lp
. This implies that the job
owner, the loginID which the printing client reports to CUPS, must have an
account on the CUPS/HylaFAX server.
lp ALL=(ALL) NOPASSWD: SETENV: /usr/bin/sendfax
Fax4CUPS assumes that it will not get enough useful parameters to create a cover page (q.v.), and suppresses this feature. However, if you're sending to an office it's important to let at least the individual recipient's name be shown; the needed recipient format is discussed here. You need to hack the backend script so it does not specify the -n (no cover page) switch to sendfax.
When printing, set the job name (with the -J option of lpr or -t option of lp) to the recipient (phone number). The format of the recipient is anything acceptable to HylaFAX (q.v.) Acceptable content to be printed is anything that CUPS can convert to PostScript, i.e. just about anything.
You can also set these CUPS options using the -o switch of lp and lpr:
normalway to specify the recipient)
It is assumed that a web server runs on the same machine that hosts HylaFAX. Faxy is a PHP script which is a front end to HylaFAX. It can display FAXes that have been received, archive or delete them, or accept an uploaded content file (text/plain, PostScript, or PDF) and send it as a FAX. Button labels and documentation are in Italian.
Actually it's a pretty nice program if you have some modest ability to work with languages you don't know. I do have some criticisms, which are given at the end of this document. To install, you need to edit the configuration file .../faxy/config.php.
When our Windows user wants to work with FAXes, Faxy is going to be the main interface to HylaFAX.
Faxy's installation instructions assume that the webserver will use mod_php5 (for Apache, or the equivalent on other webservers). However, for security reasons I execute PHP as a CGI script. To make this work you need to make the Faxy program units executable (chmod a+rx .../faxy/*.php), and you need to register PHP with binfmt_misc (described at the end of this document).
This program communicates across the net with the HylaFAX server.
The documentation says it needs server version 4.0 through 4.3; I suspect
this means it uses the new protocol
and will work on 4.0 and up.
It can execute on Windows NT, 2000, XP and Vista. The current version is
able to display FAXes if a TIFF viewer is installed (see the above URL for
recommended viewers).
I installed it successfully (on Vista), and it probably would have
functioned properly in a less demanding environment. However, the server has
an aggressive firewall, and HylaFAX uses the FTP protocol on its own port;
evidently it's smart enough to use passive FTP when the active style is not
possible. But the server also blocked passive connections from the client
to arbitrary server ports. I was not able to figure out an iptables rule
(using the helper
module) that would apply the FTP helper to the
HylaFAX port.
So WHFC is going to have to wait until the firewall problem is solved.
This is a printer emulator for Windows that sends jobs to HylaFAX. It can pop a dialog box to ask for the recipient (phone number); see here for formatting. However, it is advertised as working on Windows NT through XP. On Vista it tries to create its special port and gets permission denied. So I won't be able to use Winprint HylaFAX.
Get the server's firewall opened up. Then try WHFC again.
For Faxy's PHP I need to improve how binfmt_misc is activated. [Done.]
Set up a more snazzy cover page; the one SuSE gives you is rather boring.
My criticisms of Faxy should be backed up by some work to fix them.
In many offices FAXes are read on the server only, but we're low tech and we would invariably want to print an incoming FAX. This can be set up to happen automatically.
The most common scenario in our office is to scan a hardcopy document and send it out as a FAX. This is always a traumatic experience, re-learning the scanning software. We need some automation for this task, particularly since none of the FAX software accepts graphic formats natively.
Several bug reports are mentioned above. File them.
Several packages create log files: HylaFAX, fax4CUPS and Faxy. I'm not sure if they're all getting rotated or cleaned up properly.
Investigate using a UserDir (~fax/public_html) to deal with file permission issues with Faxy. The recommended adjustments to group permissions seem a little easy to break in upgrades and may not be adequate in a context of paranoid security for incoming FAXes.
TDC in the UK is a vendor of modular modems to be included in embedded systems. Look about 65% through this page; there's a list of modern AT commands. Strictly speaking, "N Baud" means the rate of signalling units per sec. With fancy encoding you can pack multiple bits per signalling unit, so it is not correct to equate bits/sec with baud.
When using it for the first time, execute as root: minicom -s
.
Edit settings as appropriate, then save, which creates the systemwide
configuration file /etc/minirc.dfl. In particular, you specify here the
serial line to which the modem is attached: for me, /dev/ttyUSB0 or its
symbolic link /dev/faxmodem0. Once /etc/minirc.dfl is created you can just
invoke it as minicom
, no command line arguments.
Once minicom is started you can use these escape sequences. The letters are not case sensitive.
However, ^AH and ^AX do not seem to actually hang it up; you need to tell the modem ATH0.
Faxy is a nice program and does pretty much what I want, but I've found some problems with it.
Obviously, it's in Italian and I don't know Italian. But I can fake it from cultural experience and Latin cognates. The author says he's working on an English translation. For that, a pluggable translation package would be a big help, but I don't see one in the PHP manual.
Faxy receives requests from other hosts, which must be assumed to be hostile. Request parameters are used to construct shell commands such as sendfax. All request parameters need to be sanitized, at least by escaping shell-active characters and/or by enclosing them in quotes. These must be double quotes, because some of the fields contain free-form text that is likely to contain apostrophes (single quotes), which cannot be escaped within single quotes.
Faxy advertises being able to send PDF files, and specifically tests
for this mime-type. In fact, sendfax can natively translate generic PostScript
and text/plain as well. These mime-types should be accomodated. In addition,
scanned documents generally come out as PNG or JPEG, and automatic conversion
of graphic formats would be most welcome: perhaps using the convert
program from ImageMagick.
The log files come out as one file per FAX sent, making a problem for rotation or cleanup. There should be one big log file, and a control file for /etc/logrotate.d should be included.
Faxy is a set of PHP programs. For security reasons I do not run mod_php5 on my Apache webserver; rather I execute PHP scripts using a self-compiled interpreter for the CGI SAPI. (My distro does not understand the importance of doing this when a variety of non-cooperating users have UserDirs on one webserver -- but that's another rant.)
The most convenient way to make the CGI SAPI happen is to make the PHP scripts executable (chmod a+rx .../faxy/*.php) and then activate binfmt_misc. You need to do these steps:
I made an init script in the SuSE/Red Hat/SysV style to do this at boot time.
This project was not started with voice in mind. However, the Conexant CX93010 chipset turns out to have voice capability, intended to turn your computer into a telephone answering machine. Beyond that, if it can answer (terminate) a call, dial (originate) a call, and send and receive voice in either circumstance, then this modem is a legendary FXO POTS bridge enabling VOIP service on the home server: just like Skype but locally controlled and lacking the negative political and protocol aspects of the commercial company.
In case I want to invest the time to make this work, here are notes on the research I've done so far.
This howto on modem multiplexing (dated 2001-02-27) is frequently linked to. Here's a summary of what to do to serve data, FAX and voice on a suitably equipped modem.
Install mgetty. It multiplexes data, incoming FAX, and voice. It's in the SuSE distro.
cp $source/voice.conf-dist /etc/mgetty+sendfax/voice.conf The file has comments. Do what they say. Particular parms:
Edit inittab and start vgetty (not mgetty) on that tty.
Create the announcement. It wants mono 7200Hz (varies among modems) in rmd format, whatever that is. The program expects the announcement as $voice_dir/standard.rmd Or you can set up multiple greeting files.
Mgetty includes a utility vm record filename.rmd
to copy mic
input.
Or convert a file in another format:
wavtopvf input.wav output.pvf pvfspeed -s 7200 input.pvf output.pvf pvfspeed -L (or pvftormd -L?) for a list of supported modems. pvftormd Rockwell 4 input.pvf output.rmd (if the sample rate is wrong it will tell you what it wants.) 4 = bits per sample, higher is better but bigger.
Incoming messages go in $voice_dir/incoming/. You can play them out the modem by: "vm play filename" To play them on the sound card:
rmdtopvf filename.rmd | pvfspeed -s 8000 | pvftobasic > /dev/audio
Check out VOCP for a fancier setup. VOCP looks like it has a lot of functions we want, including:
For full-blown VOIP, Asterisk is the benchmark open-source PBX program.