Saturday, September 13, 2008

Samba tutorial - Part 11

Using Samba Across Routed Networks

Andrew Tridgell states that SMB host browsing across routers is problematic. Here are his suggestions to allow this:

-------------------------------------------------------------- For cross-subnet (ie. routed) browsing you should do the following. There are other methods but they are much more complex are error prone:

1) all computers that you want visible should use a single WINS server (Samba or NT can do this)

2) the master browser for each subnet must be either NT or Samba. (Win9X doesn't communicate cross-subnet browse info correctly)

3) You should use the same workgroup name on all subnets. This is not strictly necessary but it is the simplest way to guarantee success. If you can't arrange this then you must organise for a way for browse info to propogate between subnets. (It does *not* propogate via WINS). It propogates via two mechanisms: i) each browse master notices workgroup announcements from other browse masters on the same broadcast domain ii) each non-Win9X browse master contacts the global DMB for the workgroup (typically the domain controller or a Samba box marked as the domain master) and swaps full browse info periodically. --------------------------------------------------------------

Also, Rakesh Bharania points out that Cisco routers can be configured to forward SMB traffic in a way that allows browsing. His suggestion is to configure the router interface which hosts SMB clients with a command like this:


ip helper-address x.x.x.x

where x.x.x.x is the IP address of the SMB server.

==================================================================

Acknowledgements

Special thanks to Andrew Tridgell ( tridge@linuxcare.com) for starting and directing the Samba project and for keeping this document honest.

Brad Marshall ( bmarshall@plugged.net.au) and Jason Parker ( jparker@plugged.net.au) contributed time, patience, scripting and research.

Adam Neat ( adamneat@ipax.com.au) and Dan Tager ( dtager@marsala.com) contributed the bash scripts used to back up Windows machines to a Linux host.

Matthew Flint () told me about the use of the 'interfaces' option in smb.conf.

Oleg L. Machulskiy ( machulsk@shade.msu.ru), Jeff Stern ( jstern@eclectic.ss.uci.edu), Dr. Michael Langner ( langner@fiz-chemie.de and Erik Ratcliffe ( erik@caldera.com) suggested modifications to the section on Sharing A Linux Printer With Windows Machines.

Alberto Menegazzi ( flash.egon@iol.it) contributed the MagicFilter setup to enable a Linux machine to share a Windows printer.

Rakesh Bharania ( rbharani@cisco.com) contributed the suggestion for Cisco router configuration.

Rich Gregory ( rtg2t@virginia.edu) and others suggested that this document show some details about the smbfs package and its use.

Andrea Girotto ( icarus@inca.dei.unipd.it) contributed a number of valuable suggestions throughout the document.

Thanks, also, to all of the international translators that have brought this HOWTO to the non-English speaking world: Takeo Nakano ( nakano@apm.seikei.ac.jp), Klaus-Dieter Schumacher ( Klaus-Dieter.Schumacher@fernuni-hagen.de), Andrea Girotto ( icarus@inca.dei.unipd.it), Mathieu Arnold ( arn_mat@club-internet.fr), Stein Oddvar Rasmussen ( Stein@kongsberg-energi.no) Nilo Menezes ( nmenezes@n3.com.br) and many others for whom I don't have contact details.

Samba tutorial - Part 10

Backing Up Windows Machines to a Linux Host

Adam Neat ( adamneat@ipax.com.au) kindly contributed the following script to back up Windows machines to a Linux host, using the smbclient utility. Adam says that it is used to backup Windows 3.x and NT machines to a Linux based DAT SCSI Drive.

Adam is not proud of the coding style used here, but it works. As I like to say, "If it works and its stupid, then it is not stupid".

Another Windows backup script, contributed by Dan Tager ( dtager@marsala.com), is provided below. Dan's script also backs up Unix machines via rsh, although that could be modified to use ssh rather easily.

In this script, the string 'agnea1' is the username on the Linux machine that does the backups.


#!/bin/bash

clear
echo Initialising ...
checkdate=`date | awk '{print $1}'`

if [ -f "~agnea1/backup-dir/backup-data" ]; then

echo "ERROR: No config file for today!"
echo "FATAL!"
exit 1
fi

if [ -d "~agnea1/backup-dir/temp" ]; then

echo "ERROR: No tempoary directory found!"
echo
echo "Attempting to create"
cd ~agnea1
cd backup-dir
mkdir temp
echo "Directory Made - temp"
fi

if [ "$1" = "" ]; then

echo "ERROR: enter in a machine name (ie: cdwriter)"
exit 1
fi

if [ "$2" = "" ]; then

echo "ERROR: enter in a SMB (Lan Manager) Resource (ie: work)"
exit 1
fi

if [ "$3" = "" ]; then

echo "ERROR: enter in an IP address for $1 (ie:
130.xxx.xxx.52)" exit 1
fi


#############################################################################
# Main Section
#
#############################################################################

cd ~agnea1/backup-dir/temp
rm -r ~agnea1/backup-dir/temp/*
cd ~agnea1/backup-dir/

case "$checkdate"
in
Mon)
echo "Backuping for Monday"
cat backup-data | /usr/local/samba/bin/smbclient
\\\\$1\\$2 -I$3 -N echo "Complete"

if [ -d "~agnea1/backup-dir/Monday" ]; then
echo "Directory Monday Not found ...
making" mkdir
~agnea1/backup-dir/Monday
fi

echo "Archiving ..."
cd ~agnea1/backup-dir/temp
tar -cf monday.tar * echo "done ..."
rm ~agnea1/backup-dir/Monday/monday.tar
mv monday.tar ~agnea1/backup-dir/Monday
;;


Tue)
echo "Backuping for Tuesday"
cat backup-data | /usr/local/samba/bin/smbclient
\\\\$1\\$2 -I$3 -N echo "Complete"

if [ -d "~agnea1/backup-dir/Tuesday" ]; then
echo "Directory Tuesday Not found ...
making" mkdir
~agnea1/backup-dir/Tuesday
fi
echo "Archiving ..."
cd ~agnea1/backup-dir/temp
tar -cf tuesday.tar *
echo "done ..."
rm ~agnea1/backup-dir/Tuesday/tuesday.tar
mv tuesday.tar ~agnea1/backup-dir/Tuesday
;;

Wed)
echo "Backuping for Wednesday"
cat backup-data | /usr/local/samba/bin/smbclient
\\\\$1\\$2 -I$3 -N echo "Complete"

if [ -d "~agnea1/backup-dir/Wednesday" ]; then
echo "Directory Wednesday Not found
... making" mkdir
~agnea1/backup-dir/Wednesday
fi
echo "Archiving ..."
cd ~agnea1/backup-dir/temp
tar -cf wednesday.tar *
echo "done ..."
rm ~agnea1/backup-dir/Wednesday/wednesday.tar
mv wednesday.tar ~agnea1/backup-dir/Wednesday
;;

Thu)
echo "Backuping for Thrusday"
cat backup-data | /usr/local/samba/bin/smbclient
\\\\$1\\$2 -I$3 -N echo "Complete"

if [ -d "~agnea1/backup-dir/Thursday" ]; then
echo "Directory Thrusday Not found ...
making" mkdir
~agnea1/backup-dir/Thursday
fi
echo "Archiving ..."
cd ~agnea1/backup-dir/temp
tar -cf thursday.tar *
echo "done ..."
rm ~agnea1/backup-dir/Thursday/thursday.tar
mv thursday.tar ~agnea1/backup-dir/Thursday
;;


Fri)
echo "Backuping for Friday"
cat backup-data | /usr/local/samba/bin/smbclient
\\\\$1\\$2 -I$3 -N echo "Complete"

if [ -d "~agnea1/backup-dir/Friday" ]; then
echo "Directory Friday Not found ...
making" mkdir
~agnea1/backup-dir/Friday
fi
echo "Archiving ..."
cd ~agnea1/backup-dir/temp
tar -cf friday.tar *
echo "done ..."
rm ~agnea1/backup-dir/Friday/friday.tar
mv friday.tar ~agnea1/backup-dir/Friday
;;

*)
echo "FATAL ERROR: Unknown variable passed for day"
exit 1;;

esac
###########

Here's Dan's backup script:


#!/bin/bash

BACKDIR=3D/backup
WINCMD=3D/usr/bin/smbclient

function CopyWinHost(){

# tars and gzips "windows shares" to a local directory using samba's
# smbclient
# argument 1 is the remote host window's host name
# argument 2 is the share name to be backed up

echo $1,$2,$3
REMOTE=3D$1
SHARE=3D$2
DEST=3D$3

# create a tarred gzip file using samba to copy direct from a
# windows pc
# 12345 is a password. Needs some password even if not defined on
# remote system
$WINCMD \\\\$REMOTE\\$SHARE 12345 -Tc -|gzip > $DEST
echo `date`": Done backing up "$REMOTE" to "$DEST
echo
}

function CopyUnixHost(){

# tars and gzips a directory using rsh
# argument 1 is the name of the remote source host
# argument 2 is the full path to the remote source directory
# argument 3 is the name of the local tar-gzip file. day of week
# plus .tgz will be appended to argument 3

REMOTE=3D$1
SRC=3D$2
DEST=3D$3


if rsh $REMOTE tar -cf - $SRC |gzip > $DEST; then
echo `date`": Done backing up "$REMOTE":"$SRC" to "$DEST
else
echo `date`": Error backing up "$REMOTE":"$SRC" to "$DEST
fi

}

# $1: win=3Dbackup windows machine, unix=3Dbackup unix machine
case $1 in
win)
# $2=3D remote windows name, $3=3Dremote share name,
# $4=3Dlocal destination directory
CopyWinHost $2 $3 $4;;
unix)
# $2 =3D remote host, $3 =3D remote directory,
# $4 =3D destination name
CopyUnixHost $2 $3 $4;;
esac

Samba tutorial - Part 9

Sharing A Windows Printer With Linux Machines

To share a printer on a Windows machine, you must do the following:

  1. You must have the proper entries in /etc/printcap and they must correspond to the local directory structure (for the spool directory, etc).
  2. You must have the script /usr/bin/smbprint. This comes with the Samba source, but not with all Samba binary distributions. A slightly modifed copy is discussed below.
  3. If you want to convert ASCII files to Postscript, you must have nenscript, or its equivalent. nenscript is a Postscript converter and is generally installed in /usr/bin.
  4. You may wish to make Samba printing easier by having an easy-to-use front end. A simple perl script to handle ASCII, Postscript or created Postscript is given below.
  5. You could also use MagicFilter to do the above. The details on setting up MagicFilter are given below the perl script. MagicFilter has advantages because it knows how to automatically convert a lot of file formats.

The /etc/printcap entry below is for an HP 5MP printer on a Windows NT host. The entries are as follows:


        cm - comment
lp - device name to open for output
sd - the printer's spool directory (on the local machine)
af - the accounting file
mx - the maximum file size (zero is unlimited)
if - name of the input filter (script)

For more information, see the Printing HOWTO or the man page for printcap.


# /etc/printcap
#
# //zimmerman/oreilly via smbprint
#
lp:\
:cm=HP 5MP Postscript OReilly on zimmerman:\
:lp=/dev/lp1:\
:sd=/var/spool/lpd/lp:\
:af=/var/spool/lpd/lp/acct:\
:mx#0:\
:if=/usr/bin/smbprint:

Make certain that the spool and accounting directories exist and are writable. Ensure that the 'if' line holds the proper path to the smbprint script (given below) and make sure that the proper device is pointed to (the /dev special file).

Next is the smbprint script itself. It is usually placed in /usr/bin and is attributable to Andrew Tridgell, the person who created Samba as far as I know. It comes with the Samba source distribution, but is absent from some binary distributions, so I have recreated it here.

You may wish to look at this carefully. There are some minor alterations that have shown themselves to be useful.


#!/bin/sh -x

# This script is an input filter for printcap printing on a unix machine. It
# uses the smbclient program to print the file to the specified smb-based
# server and service.
# For example you could have a printcap entry like this
#
# smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
#
# which would create a unix printer called "smb" that will print via this
# script. You will need to create the spool directory /usr/spool/smb with
# appropriate permissions and ownerships for your system.

# Set these to the server and service you wish to print to
# In this example I have a WfWg PC called "lapland" that has a printer
# exported called "printer" with no password.

#
# Script further altered by hamiltom@ecnz.co.nz (Michael Hamilton)
# so that the server, service, and password can be read from
# a /usr/var/spool/lpd/PRINTNAME/.config file.
#
# In order for this to work the /etc/printcap entry must include an
# accounting file (af=...):
#
# cdcolour:\
# :cm=CD IBM Colorjet on 6th:\
# :sd=/var/spool/lpd/cdcolour:\
# :af=/var/spool/lpd/cdcolour/acct:\
# :if=/usr/local/etc/smbprint:\
# :mx=0:\
# :lp=/dev/null:
#
# The /usr/var/spool/lpd/PRINTNAME/.config file should contain:
# server=PC_SERVER
# service=PR_SHARENAME
# password="password"
#
# E.g.
# server=PAULS_PC
# service=CJET_371
# password=""

#
# Debugging log file, change to /dev/null if you like.
#
logfile=/tmp/smb-print.log
# logfile=/dev/null


#
# The last parameter to the filter is the accounting file name.
#
spool_dir=/var/spool/lpd/lp
config_file=$spool_dir/.config

# Should read the following variables set in the config file:
# server
# service
# password
# user
eval `cat $config_file`

#
# Some debugging help, change the >> to > if you want to same space.
#
echo "server $server, service $service" >> $logfile

(
# NOTE You may wish to add the line `echo translate' if you want automatic
# CR/LF translation when printing.
echo translate
echo "print -"
cat
) | /usr/bin/smbclient "\\\\$server\\$service" $password -U $user -N -P >> $logfile

Most Linux distributions come with nenscript for converting ASCII documents to Postscript. The following perl script makes life easier be providing a simple interface to Linux printing via smbprint.


Usage: print [-a|c|p] 
-a prints as ASCII
-c prints formatted as source code
-p prints as Postscript
If no switch is given, print attempts to
guess the file type and print appropriately.

Using smbprint to print ASCII files tends to truncate long lines. This script breaks long lines on whitespace (instead of in the middle of a word), if possible.

The source code formatting is done with nenscript. It takes an ASCII file and foramts it in 2 columns with a fancy header (date, filename, etc). It also numbers the lines. Using this as an example, other types of formatting can be accomplished.

Postscript documents are already properly formatted, so they pass through directly.


#!/usr/bin/perl

# Script: print
# Authors: Brad Marshall, David Wood
# Plugged In Communications
# Date: 960808
#
# Script to print to a Postscript printer via Samba.
# Purpose: Takes files of various types as arguments and
# processes them appropriately for piping to a Samba print script.
#
# Currently supported file types:
#
# ASCII - ensures that lines longer than $line_length characters wrap on
# whitespace.
# Postscript - Takes no action.
# Code - Formats in Postscript (using nenscript) to display
# properly (landscape, font, etc).
#

# Set the maximum allowable length for each line of ASCII text.
$line_length = 76;

# Set the path and name of the Samba print script
$print_prog = "/usr/bin/smbprint";

# Set the path and name to nenscript (the ASCII-->Postscript converter)
$nenscript = "/usr/bin/nenscript";

unless ( -f $print_prog ) {
die "Can't find $print_prog!";
}
unless ( -f $nenscript ) {
die "Can't find $nenscript!";
}

&ParseCmdLine(@ARGV);

# DBG
print "filetype is $filetype\n";

if ($filetype eq "ASCII") {
&wrap($line_length);
} elsif ($filetype eq "code") {
&codeformat;
} elsif ($filetype eq "ps") {
&createarray;
} else {
print "Sorry..no known file type.\n";
exit 0;
}
# Pipe the array to smbprint
open(PRINTER, "|$print_prog") || die "Can't open $print_prog: $!\n";
foreach $line (@newlines) {
print PRINTER $line;
}
# Send an extra linefeed in case a file has an incomplete last line.
print PRINTER "\n";
close(PRINTER);
print "Completed\n";
exit 0;

# --------------------------------------------------- #
# Everything below here is a subroutine #
# --------------------------------------------------- #

sub ParseCmdLine {
# Parses the command line, finding out what file type the file is

# Gets $arg and $file to be the arguments (if the exists)
# and the filename
if ($#_ < 0) {
&usage;
}
# DBG
# foreach $element (@_) {
# print "*$element* \n";
# }

$arg = shift(@_);
if ($arg =~ /\-./) {
$cmd = $arg;
# DBG
# print "\$cmd found.\n";

$file = shift(@_);
} else {
$file = $arg;
}

# Defining the file type
unless ($cmd) {
# We have no arguments

if ($file =~ /\.ps$/) {
$filetype = "ps";
} elsif ($file =~ /\.java$|\.c$|\.h$|\.pl$|\.sh$|\.csh$|\.m4$|\.inc$|\.html$|\.htm$/) {
$filetype = "code";
} else {
$filetype = "ASCII";
}

# Process $file for what type is it and return $filetype
} else {
# We have what type it is in $arg
if ($cmd =~ /^-p$/) {
$filetype = "ps";
} elsif ($cmd =~ /^-c$/) {
$filetype = "code";
} elsif ($cmd =~ /^-a$/) {
$filetype = "ASCII"
}
}
}

sub usage {
print "
Usage: print [-a|c|p]
-a prints as ASCII
-c prints formatted as source code
-p prints as Postscript
If no switch is given, print attempts to
guess the file type and print appropriately.\n
";
exit(0);
}

sub wrap {
# Create an array of file lines, where each line is < the
# number of characters specified, and wrapped only on whitespace

# Get the number of characters to limit the line to.
$limit = pop(@_);

# DBG
#print "Entering subroutine wrap\n";
#print "The line length limit is $limit\n";

# Read in the file, parse and put into an array.
open(FILE, "<$file") || die "Can't open $file: $!\n";
while() {
$line = $_;

# DBG
#print "The line is:\n$line\n";

# Wrap the line if it is over the limit.
while ( length($line) > $limit ) {

# DBG
#print "Wrapping...";

# Get the first $limit +1 characters.
$part = substr($line,0,$limit +1);

# DBG
#print "The partial line is:\n$part\n";

# Check to see if the last character is a space.
$last_char = substr($part,-1, 1);
if ( " " eq $last_char ) {
# If it is, print the rest.

# DBG
#print "The last character was a space\n";

substr($line,0,$limit + 1) = "";
substr($part,-1,1) = "";
push(@newlines,"$part\n");
} else {
# If it is not, find the last space in the
# sub-line and print up to there.

# DBG
#print "The last character was not a space\n";

# Remove the character past $limit
substr($part,-1,1) = "";
# Reverse the line to make it easy to find
# the last space.
$revpart = reverse($part);
$index = index($revpart," ");
if ( $index > 0 ) {
substr($line,0,$limit-$index) = "";
push(@newlines,substr($part,0,$limit-$index)
. "\n");
} else {
# There was no space in the line, so
# print it up to $limit.
substr($line,0,$limit) = "";
push(@newlines,substr($part,0,$limit)
. "\n");
}
}
}
push(@newlines,$line);
}
close(FILE);
}

sub codeformat {
# Call subroutine wrap then filter through nenscript
&wrap($line_length);

# Pipe the results through nenscript to create a Postscript
# file that adheres to some decent format for printing
# source code (landscape, Courier font, line numbers).
# Print this to a temporary file first.
$tmpfile = "/tmp/nenscript$$";
open(FILE, "|$nenscript -2G -i$file -N -p$tmpfile -r") ||
die "Can't open nenscript: $!\n";
foreach $line (@newlines) {
print FILE $line;
}
close(FILE);

# Read the temporary file back into an array so it can be
# passed to the Samba print script.
@newlines = ("");
open(FILE, "<$tmpfile") || die "Can't open $file: $!\n";
while() {
push(@newlines,$_);
}
close(FILE);
system("rm $tmpfile");
}

sub createarray {
# Create the array for postscript
open(FILE, "<$file") || die "Can't open $file: $!\n";
while() {
push(@newlines,$_);
}
close(FILE);
}

Now the MagicFilter way. Thanks to Alberto Menegazzi ( flash.egon@iol.it) for this information.

Alberto says:

-------------------------------------------------------------- 1) Install MagicFilter with the filter for the printers you need in /usr/bin/local but DON'T fill /etc/printcap with the suggestion given by the documentation from MagicFilter.

2) Write the /etc/printcap like this way (it's done for my LaserJet 4L):

lp|ljet4l:\ :cm=HP LaserJet 4L:\ :lp=/dev/null:\ # or /dev/lp1 :sd=/var/spool/lpd/ljet4l:\ :af=/var/spool/lpd/ljet4l/acct:\ :sh:mx#0:\ :if=/usr/local/bin/main-filter:

You should explain that the lp=/dev/... is opened for locking so "virtual" devices one for every remote printer should be used.

Example creating with : touch /dev/ljet4l

3) Write the filter /usr/local/bin/main-filter the same you suggest using the ljet4l-filter instead of cat.

Here's mine.

#! /bin/sh logfile=/var/log/smb-print.log spool_dir=/var/spool/lpd/ljet4l ( echo "print -" /usr/local/bin/ljet4l-filter ) | /usr/bin/smbclient "\\\\SHIR\\HPLJ4" -N -P >> $logfile

P.S. : here is the quote from the Print2Win mini-Howto about locking and why creating virtual printers

---Starts here

Hint from Rick Bressler :

Good tip sheet. I use something very similar. One helpful tip, this is not a particularly good idea:

:lp=/dev/null:\

lpr does an 'exclusive' open on the file you specify as lp=. It does this in order to prevent multiple processes from trying to print to the dame printer at the same time.

The side effect of this is that in your case, eng and colour can't print at the same time, (usually more or less transparent since they probably print quickly and since they queue you probably don't notice) but any other process that tries to write to /dev/null will break!

On a single user system, probably not a big problem. I have a system with over 50 printers. It would be a problem there.

The solution is to create a dummy printer for each. Eg: touch /dev/eng.

I have modified the lp entries in the printcap file above to take into account Rick's suggestion. I did the following:

#touch /dev/eng #touch /dev/colour

---Ends here

Your Ad Here