Questions
and Answers
Amy Rich
Q I'm using the version of Perl that came with
Solaris 8. I tried to install a new Perl module, but the install failed because
the Perl that Sun shipped was built with Sun's C compiler. I'm not about to
fork over a good chunk of money to buy a license when I can get gcc for free.
But, do I need to recompile Perl from scratch, or is there a way to work around
this issue?
A Sun's C complier does have some benefits
over gcc on the SPARC platform, but if you don't want to purchase the license,
there appears to be a Perl module on CPAN that will allow you to use Sun's existing
Perl with gcc:
http://cpan.org/modules/by-authors/id/ABURLISON/
Solaris/PerlGcc version 1.1
============================
perlgcc - Compile perl modules using gcc.
The perl 5.005_03 and 5.6.1 shipped with Solaris 8, 9 and 10 were built with
the Forte compilers, and therefore assume that any add-on modules will also
be built with the Forte compiler. The perlgcc command will allow gcc
to be used to compile add-on modules for use with the perl versions shipped
with Solaris.
INSTALLATION
$ perl Makefile.PL; make install
USAGE
$ perlgcc Makefile.PL; make install
$ perlgcc -MCPAN -e shell
Q I'm running BIND 9 and have a problem with my
secondary server not being able to successfully transfer a zone from the primary.
I can see that the primary server is allowing the transfer, but on the secondary
server I get the following error messages:
named[4175]: dumping master file: /etc/namedb/tmp-XXXXIt5Dc3: open:
permission denied
named[5327]: transfer of 'my.domain/IN' from 192.168.1.1#53: failed
while receiving responses: permission denied
named[5327]: transfer of 'my.domain/IN' from 192.168.1.1#53: end of
transfer
I'm stumped as to what the issue is, since the named.conf on both machines looks
just fine as far as I can tell. The primary server (192.168.1.1) has:
zone "my.domain" {
type master;
file "my.domain";
allow-transfer {
192.168.1.2;
};
};
The secondary server (192.168.1.2) has:
zone "my.domain" {
type slave;
file "my.domain";
masters {
192.168.1.1;
};
};
Any pointers would be greatly appreciated.
A Your issue is with the file/directory permissions
on the secondary server. Presumably you've set up bind to run as a non-root
user. This is a good idea for security purposes, but you need to make sure that
this non-root user can write to the directory in which your zone files reside.
If you change the permissions of /etc/namedb so that your bind user can create
files (and you make sure that the bind user also owns the existing zone files
in that directory), the problem reported by your error messages should be solved.
Q We need to do a bunch of maintenance work
on a Sun 220R running Solaris 8, and I want to change around the disk controller
hardware without incrementing the controller numbers. Is there a way to make
it "forget" that it previously allocated a controller number to another I/O
slot? I want to continue using c0, c1, c2, and c3. I'm trying to avoid winding
up with things like c4, c5, c6, and c7.
A If the boot disk does not hang off the specified
controller, you can do the following:
- Remove the /etc/path_to_inst files:
mv /etc/path_to_inst /etc/path_to_inst.orig
rm /etc/path_to_inst.old
- Remove all the devices except the boot device in both /dev/dsk and /dev/rdsk
(I'll assume your boot disk is at c0):
cd /dev/dsk
rm c1* c2* c3* c4* (etc)
cd /dev/rdsk
rm c1* c2* c3* c4* (etc)
- Make sure to record the device name of the boot disk if it's under Volume
Manager. If you look in /etc/system, there should be a line that starts with
"rootdev:".
- Shut down the system and boot interactively:
init 0
boot -ar
Since /etc/path_to_inst does not exist, the interactive boot will ask you for
the location and name of all the important system files. You should be able to
accept the default for all of these files (just hit return) except when it asks
you if you want to rebuild /etc/path_to_inst. Here you should type Y and hit return.
If you have the boot disk under Volume Manager, do not take the default when asked
for the physical root device, instead, enter the device name you recorded from
/etc/system.
If the boot disk is attached to the controller you want to change, then you
can boot from the CDROM and then make changes to said boot disk:
boot cdrom -sw
mount /dev/dsk/c0t3d0s0 /a
cd /a/dev/dsk
rm c*
cd /a/dev/rdsk
rm c*
cd /
devfsadm -r /a
cd /
umount /a
reboot
You may also find Sun Infodoc ID 13774 useful if you have a SPARC Storage Array.
Q I'm a C programmer trying to write a Perl
program where I push an element onto a multidimensional array, but I can't seem
to get this to work correctly. Here's the code I'm using:
$myarray[0][0] = 'hello';
push $myarray[0], 'world';
I'm trying to wind up with:
$myarray[0][1] = 'world';
Obviously, I'm not doing this right, but Perl must have something as simple as
a multidimensional array. Any hints on how I can get my code working?
A Perl is a bit different from C, as I'm sure
you've discovered. In Perl, $array[0] is not an array, but an array reference.
An important note to remember from the Perl documentation: "Perl does no implicit
referencing or dereferencing. When a scalar is holding a reference, it always
behaves as a simple scalar. It doesn't magically start being an array or hash
or subroutine; you have to tell it explicitly to do so, by dereferencing it."
For information on Perl's use of references, do:
perldoc perlref
perldoc perlreftut
The section most pertinent to your question can be found in the Perl reference
tutorial:
Let's see a quick example of how all this is useful.
First, remember that [1, 2, 3] makes an anonymous array containing (1, 2,
3), and gives you a reference to that array.
Now think about
@a = ( [1, 2, 3],
[4, 5, 6],
[7, 8, 9]
);
@a is an array with three elements, and each one is a reference to another
array.
$a[1] is one of these references. It refers to an array, the array containing
(4, 5, 6), and because it is a reference to an array, USE RULE 2 says that we
can write $a[1]->[2] to get the third element from that array. $a[1]->[2]
is the 6. Similarly, $a[0]->[1] is the 2. What we have here is like a two-dimensional
array; you can write $a[ROW]->[COLUMN] to get or set the element in any row
and any column of the array.
The notation still looks a little cumbersome, so there's one more abbreviation:
Arrow Rule In between two subscripts, the arrow is optional.
Instead of $a[1]->[2], we can write $a[1][2]; it means the same thing.
Instead of $a[0]->[1], we can write $a[0][1]; it means the same thing.
Now it really looks like two-dimensional arrays!
You can see why the arrows are important. Without them, we would have had
to write ${$a[1]}[2] instead of $a[1][2]. For three-dimensional arrays, they
let us write $x[2][3][5] instead of the unreadable ${${$x[2]}[3]}[5].
Anywhere you'd put an identifier as part of a variable or subroutine name,
you can replace the identifier with a block returning a reference of the correct
type:
$myarray[0][0] = 'hello';
push @{$myarray[0]}, 'world';
You can print out your "hello world" statement by using the same notation as the
push:
print "myarray == @{$myarray[0]}.\n";
Q I'm running Solaris 8 and Sun's stock sendmail
(8.11.6). I have about 30 lines of denied addresses and IPs in /etc/mail/access.
The problem is that, because my connection to the Internet is rather poor, a good
chunk of my mail goes through two lower-priority MX hosts on which I do not have
administrative control. Unfortunately, this means that all of the mail that gets
blocked on Connect to my machine now gets through.
Is there any way to get around this? Is there some sort of per-host (or domain)
way to apply access lists? If so, then I can request that the admins of the
MX hosts just use this setting to apply my access rules to my mail coming through
their machines?
A Unfortunately, access rules apply to all
incoming mail and there is no way to apply rules on a per-host or domain basis.
This would be quite useful for people who are doing virtual hosting of many
domains, though. In your case, it might be more trouble than it's worth if you're
frequently updating your access list. You'd not only have to modify your own,
but then send a copy out to all of your MX hosts as well.
Though you won't be able to reject on Connect, you could write a ruleset to
bounce mail back based on the received headers. Below lies some code from Greg
Shapiro at Sendmail, Inc. that you could add to your mc file (note that the
syntax is different for the current version of sendmail, 8.12).
Replace the hosts in the MXers class, C{MXers}, with any hosts that MX for
you. Remember to generate a new /etc/mail/sendmail.cf from the mc file and HUP
the sendmail daemon.
FEATURE('access_db')dnl
LOCAL_CONFIG
HReceived: $>+CheckRecvd
C{MXers}your.first.mxhost your.second.mxhost
LOCAL_RULESETS
SCheckRecvd
Rfrom $* ( $+ [ $+ ] ) by $={MXers} $* $: <?> $2 $| $3
R<?> $+ $| $+ $: $>LookUpDomain <$1> <?> <$2> <+ Connect>
R<?> <$+> $: $>LookUpAddress <$1> <?> <> <+ Connect>
R<?> $* $@ok
R<$={Accept}> <$*> $@ $1
R<<TMPF>> $* $#error $@ tempfail $: "450 Temporary lookup failure"
R<REJECT> $* $#error $@ 5.7.1 $: "550 Access denied"
R<DISCARD> <$*> $#discard $: discard
R<ERROR:$-.$-.$-:$+> $* $#error $@ $1.$2.$3 $: $4
R<ERROR:$+> $* $#error $: $1
R<$+> $* $#error $: $1
If the message came to your machine from one of the machines listed in the MXers
class, then the above ruleset will take the host and IP information from the previous
relaying machine by parsing the Received: header. It will then check the access
file with the "new" information, and reject it if necessary. I suggest that your
MX hosts turn off DoubleBounceAddress if you choose to use this ruleset. Most
spam you get will have forged headers and no valid address capable of accepting
a bounce back.
Q Due to a runaway program that one of our junior
people was working on, we're left with a very large directory structure that
has some valid data in it (which we don't want deleted), and a lot of empty
directories, or directories that only contain other empty directories. These
levels of nested directories are at least 20 deep in some cases, and there are
thousands of top-level directories to start with. What's the best way to remove
all of the directories that have no leaf nodes (a.k.a. files) somewhere along
the way?
A Find is the perfect tool for this job. Because
you have nested subdirectories with no actual data (files) in them, you want
to do a depth-first search and removal. Depending on your operating system,
the command might be:
find /starting/dir/name -d -type d -exec rmdir {} \;
as with FreeBSD, in the case of Solaris and GNU-like versions of find:
find /starting/dir/name -depth -type d -exec rmdir {} \;
You'll almost certainly want to redirect STDERR to /dev/null since it sounds like
there's a lot of "good" directories that you're going to receive errors on when
you try to use rmdir.
If, for some reason, you know all of the "bad" leafnode directories, you can
also use rmdir -p for each of them. This is much more time consuming
and more prone to error, though.
Q Solaris package creation seems like a big
pain. When building third-party software from source, sometimes it's easy to
install into a different root and sometimes it's not. Is there any software
out there that can take all or even most source bundles and make packages from
them?
A I've heard good things about Simes's PkgTools:
http://www.bpfh.net/computing/software/pkg-tools/
PkgTools requires Solaris 2.6 (or later) and Perl, and it's useful to have the
Digest::MD5 Perl module.
Amy Rich, president of the Boston-based Oceanwave Consulting, Inc. (http://www.oceanwave.com),
has been a UNIX systems administrator for more than 10 years. She received a
BSCS at Worcester Polytechnic Institute, and can be reached at: qna@oceanwave.com.