mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to be commit 6938b5b98a
)
This commit is contained in:
commit
592dd24957
800
docs/docbook/projdoc/Browsing.sgml
Normal file
800
docs/docbook/projdoc/Browsing.sgml
Normal file
@ -0,0 +1,800 @@
|
||||
<chapter id="improved-browsing">
|
||||
<chapterinfo>
|
||||
<author>
|
||||
<affiliation>
|
||||
<orgname>Samba Team</orgname>
|
||||
</affiliation>
|
||||
</author>
|
||||
|
||||
|
||||
<pubdate> (5 July 1998) </pubdate>
|
||||
</chapterinfo>
|
||||
|
||||
<title>Improved browsing in samba</title>
|
||||
|
||||
<sect1>
|
||||
<title>Overview of browsing</title>
|
||||
|
||||
<para>
|
||||
SMB networking provides a mechanism by which clients can access a list
|
||||
of machines in a network, a so-called "browse list". This list
|
||||
contains machines that are ready to offer file and/or print services
|
||||
to other machines within the network. Thus it does not include
|
||||
machines which aren't currently able to do server tasks. The browse
|
||||
list is heavily used by all SMB clients. Configuration of SMB
|
||||
browsing has been problematic for some Samba users, hence this
|
||||
document.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Browsing will NOT work if name resolution from NetBIOS names to IP
|
||||
addresses does not function correctly. Use of a WINS server is highly
|
||||
recommended to aid the resolution of NetBIOS (SMB) names to IP addresses.
|
||||
WINS allows remote segment clients to obtain NetBIOS name_type information
|
||||
that can NOT be provided by any other means of name resolution.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Browsing support in samba</title>
|
||||
|
||||
<para>
|
||||
Samba now fully supports browsing. The browsing is supported by nmbd
|
||||
and is also controlled by options in the smb.conf file (see smb.conf(5)).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Samba can act as a local browse master for a workgroup and the ability
|
||||
for samba to support domain logons and scripts is now available. See
|
||||
DOMAIN.txt for more information on domain logons.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Samba can also act as a domain master browser for a workgroup. This
|
||||
means that it will collate lists from local browse masters into a
|
||||
wide area network server list. In order for browse clients to
|
||||
resolve the names they may find in this list, it is recommended that
|
||||
both samba and your clients use a WINS server.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that you should NOT set Samba to be the domain master for a
|
||||
workgroup that has the same name as an NT Domain: on each wide area
|
||||
network, you must only ever have one domain master browser per workgroup,
|
||||
regardless of whether it is NT, Samba or any other type of domain master
|
||||
that is providing this service.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
[Note that nmbd can be configured as a WINS server, but it is not
|
||||
necessary to specifically use samba as your WINS server. NTAS can
|
||||
be configured as your WINS server. In a mixed NT server and
|
||||
samba environment on a Wide Area Network, it is recommended that
|
||||
you use the NT server's WINS server capabilities. In a samba-only
|
||||
environment, it is recommended that you use one and only one nmbd
|
||||
as your WINS server].
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To get browsing to work you need to run nmbd as usual, but will need
|
||||
to use the "workgroup" option in smb.conf to control what workgroup
|
||||
Samba becomes a part of.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Samba also has a useful option for a Samba server to offer itself for
|
||||
browsing on another subnet. It is recommended that this option is only
|
||||
used for 'unusual' purposes: announcements over the internet, for
|
||||
example. See "remote announce" in the smb.conf man page.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Problem resolution</title>
|
||||
|
||||
<para>
|
||||
If something doesn't work then hopefully the log.nmb file will help
|
||||
you track down the problem. Try a debug level of 2 or 3 for finding
|
||||
problems. Also note that the current browse list usually gets stored
|
||||
in text form in a file called browse.dat.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that if it doesn't work for you, then you should still be able to
|
||||
type the server name as \\SERVER in filemanager then hit enter and
|
||||
filemanager should display the list of available shares.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Some people find browsing fails because they don't have the global
|
||||
"guest account" set to a valid account. Remember that the IPC$
|
||||
connection that lists the shares is done as guest, and thus you must
|
||||
have a valid guest account.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Also, a lot of people are getting bitten by the problem of too many
|
||||
parameters on the command line of nmbd in inetd.conf. This trick is to
|
||||
not use spaces between the option and the parameter (eg: -d2 instead
|
||||
of -d 2), and to not use the -B and -N options. New versions of nmbd
|
||||
are now far more likely to correctly find your broadcast and network
|
||||
address, so in most cases these aren't needed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The other big problem people have is that their broadcast address,
|
||||
netmask or IP address is wrong (specified with the "interfaces" option
|
||||
in smb.conf)
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Browsing across subnets</title>
|
||||
<para>
|
||||
With the release of Samba 1.9.17(alpha1 and above) Samba has been
|
||||
updated to enable it to support the replication of browse lists
|
||||
across subnet boundaries. New code and options have been added to
|
||||
achieve this. This section describes how to set this feature up
|
||||
in different settings.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To see browse lists that span TCP/IP subnets (ie. networks separated
|
||||
by routers that don't pass broadcast traffic) you must set up at least
|
||||
one WINS server. The WINS server acts as a DNS for NetBIOS names, allowing
|
||||
NetBIOS name to IP address translation to be done by doing a direct
|
||||
query of the WINS server. This is done via a directed UDP packet on
|
||||
port 137 to the WINS server machine. The reason for a WINS server is
|
||||
that by default, all NetBIOS name to IP address translation is done
|
||||
by broadcasts from the querying machine. This means that machines
|
||||
on one subnet will not be able to resolve the names of machines on
|
||||
another subnet without using a WINS server.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Remember, for browsing across subnets to work correctly, all machines,
|
||||
be they Windows 95, Windows NT, or Samba servers must have the IP address
|
||||
of a WINS server given to them by a DHCP server, or by manual configuration
|
||||
(for Win95 and WinNT, this is in the TCP/IP Properties, under Network
|
||||
settings) for Samba this is in the smb.conf file.
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title>How does cross subnet browsing work ?</title>
|
||||
|
||||
<para>
|
||||
Cross subnet browsing is a complicated dance, containing multiple
|
||||
moving parts. It has taken Microsoft several years to get the code
|
||||
that achieves this correct, and Samba lags behind in some areas.
|
||||
However, with the 1.9.17 release, Samba is capable of cross subnet
|
||||
browsing when configured correctly.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Consider a network set up as follows :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
(DMB)
|
||||
N1_A N1_B N1_C N1_D N1_E
|
||||
| | | | |
|
||||
-------------------------------------------------------
|
||||
| subnet 1 |
|
||||
+---+ +---+
|
||||
|R1 | Router 1 Router 2 |R2 |
|
||||
+---+ +---+
|
||||
| |
|
||||
| subnet 2 subnet 3 |
|
||||
-------------------------- ------------------------------------
|
||||
| | | | | | | |
|
||||
N2_A N2_B N2_C N2_D N3_A N3_B N3_C N3_D
|
||||
(WINS)
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Consisting of 3 subnets (1, 2, 3) connected by two routers
|
||||
(R1, R2) - these do not pass broadcasts. Subnet 1 has 5 machines
|
||||
on it, subnet 2 has 4 machines, subnet 3 has 4 machines. Assume
|
||||
for the moment that all these machines are configured to be in the
|
||||
same workgroup (for simplicities sake). Machine N1_C on subnet 1
|
||||
is configured as Domain Master Browser (ie. it will collate the
|
||||
browse lists for the workgroup). Machine N2_D is configured as
|
||||
WINS server and all the other machines are configured to register
|
||||
their NetBIOS names with it.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As all these machines are booted up, elections for master browsers
|
||||
will take place on each of the three subnets. Assume that machine
|
||||
N1_C wins on subnet 1, N2_B wins on subnet 2, and N3_D wins on
|
||||
subnet 3 - these machines are known as local master browsers for
|
||||
their particular subnet. N1_C has an advantage in winning as the
|
||||
local master browser on subnet 1 as it is set up as Domain Master
|
||||
Browser.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
On each of the three networks, machines that are configured to
|
||||
offer sharing services will broadcast that they are offering
|
||||
these services. The local master browser on each subnet will
|
||||
receive these broadcasts and keep a record of the fact that
|
||||
the machine is offering a service. This list of records is
|
||||
the basis of the browse list. For this case, assume that
|
||||
all the machines are configured to offer services so all machines
|
||||
will be on the browse list.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For each network, the local master browser on that network is
|
||||
considered 'authoritative' for all the names it receives via
|
||||
local broadcast. This is because a machine seen by the local
|
||||
master browser via a local broadcast must be on the same
|
||||
network as the local master browser and thus is a 'trusted'
|
||||
and 'verifiable' resource. Machines on other networks that
|
||||
the local master browsers learn about when collating their
|
||||
browse lists have not been directly seen - these records are
|
||||
called 'non-authoritative'.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
At this point the browse lists look as follows (these are
|
||||
the machines you would see in your network neighborhood if
|
||||
you looked in it on a particular network right now).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
Subnet Browse Master List
|
||||
------ ------------- ----
|
||||
Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E
|
||||
|
||||
Subnet2 N2_B N2_A, N2_B, N2_C, N2_D
|
||||
|
||||
Subnet3 N3_D N3_A, N3_B, N3_C, N3_D
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that at this point all the subnets are separate, no
|
||||
machine is seen across any of the subnets.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Now examine subnet 2. As soon as N2_B has become the local
|
||||
master browser it looks for a Domain master browser to synchronize
|
||||
its browse list with. It does this by querying the WINS server
|
||||
(N2_D) for the IP address associated with the NetBIOS name
|
||||
WORKGROUP>1B<. This name was registerd by the Domain master
|
||||
browser (N1_C) with the WINS server as soon as it was booted.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once N2_B knows the address of the Domain master browser it
|
||||
tells it that is the local master browser for subnet 2 by
|
||||
sending a MasterAnnouncement packet as a UDP port 138 packet.
|
||||
It then synchronizes with it by doing a NetServerEnum2 call. This
|
||||
tells the Domain Master Browser to send it all the server
|
||||
names it knows about. Once the domain master browser receives
|
||||
the MasterAnnouncement packet it schedules a synchronization
|
||||
request to the sender of that packet. After both synchronizations
|
||||
are done the browse lists look like :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
Subnet Browse Master List
|
||||
------ ------------- ----
|
||||
Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E,
|
||||
N2_A(*), N2_B(*), N2_C(*), N2_D(*)
|
||||
|
||||
Subnet2 N2_B N2_A, N2_B, N2_C, N2_D
|
||||
N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*)
|
||||
|
||||
Subnet3 N3_D N3_A, N3_B, N3_C, N3_D
|
||||
|
||||
Servers with a (*) after them are non-authoritative names.
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
At this point users looking in their network neighborhood on
|
||||
subnets 1 or 2 will see all the servers on both, users on
|
||||
subnet 3 will still only see the servers on their own subnet.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The same sequence of events that occured for N2_B now occurs
|
||||
for the local master browser on subnet 3 (N3_D). When it
|
||||
synchronizes browse lists with the domain master browser (N1_A)
|
||||
it gets both the server entries on subnet 1, and those on
|
||||
subnet 2. After N3_D has synchronized with N1_C and vica-versa
|
||||
the browse lists look like.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
Subnet Browse Master List
|
||||
------ ------------- ----
|
||||
Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E,
|
||||
N2_A(*), N2_B(*), N2_C(*), N2_D(*),
|
||||
N3_A(*), N3_B(*), N3_C(*), N3_D(*)
|
||||
|
||||
Subnet2 N2_B N2_A, N2_B, N2_C, N2_D
|
||||
N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*)
|
||||
|
||||
Subnet3 N3_D N3_A, N3_B, N3_C, N3_D
|
||||
N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*),
|
||||
N2_A(*), N2_B(*), N2_C(*), N2_D(*)
|
||||
|
||||
Servers with a (*) after them are non-authoritative names.
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
At this point users looking in their network neighborhood on
|
||||
subnets 1 or 3 will see all the servers on all sunbets, users on
|
||||
subnet 2 will still only see the servers on subnets 1 and 2, but not 3.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Finally, the local master browser for subnet 2 (N2_B) will sync again
|
||||
with the domain master browser (N1_C) and will recieve the missing
|
||||
server entries. Finally - and as a steady state (if no machines
|
||||
are removed or shut off) the browse lists will look like :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
Subnet Browse Master List
|
||||
------ ------------- ----
|
||||
Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E,
|
||||
N2_A(*), N2_B(*), N2_C(*), N2_D(*),
|
||||
N3_A(*), N3_B(*), N3_C(*), N3_D(*)
|
||||
|
||||
Subnet2 N2_B N2_A, N2_B, N2_C, N2_D
|
||||
N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*)
|
||||
N3_A(*), N3_B(*), N3_C(*), N3_D(*)
|
||||
|
||||
Subnet3 N3_D N3_A, N3_B, N3_C, N3_D
|
||||
N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*),
|
||||
N2_A(*), N2_B(*), N2_C(*), N2_D(*)
|
||||
|
||||
Servers with a (*) after them are non-authoritative names.
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Synchronizations between the domain master browser and local
|
||||
master browsers will continue to occur, but this should be a
|
||||
steady state situation.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If either router R1 or R2 fails the following will occur:
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Names of computers on each side of the inaccessible network fragments
|
||||
will be maintained for as long as 36 minutes, in the network neighbourhood
|
||||
lists.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Attempts to connect to these inaccessible computers will fail, but the
|
||||
names will not be removed from the network neighbourhood lists.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
If one of the fragments is cut off from the WINS server, it will only
|
||||
be able to access servers on its local subnet, by using subnet-isolated
|
||||
broadcast NetBIOS name resolution. The effects are similar to that of
|
||||
losing access to a DNS server.
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Setting up a WINS server</title>
|
||||
|
||||
<para>
|
||||
Either a Samba machine or a Windows NT Server machine may be set up
|
||||
as a WINS server. To set a Samba machine to be a WINS server you must
|
||||
add the following option to the smb.conf file on the selected machine :
|
||||
in the [globals] section add the line
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<command> wins support = yes</command>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Versions of Samba previous to 1.9.17 had this parameter default to
|
||||
yes. If you have any older versions of Samba on your network it is
|
||||
strongly suggested you upgrade to 1.9.17 or above, or at the very
|
||||
least set the parameter to 'no' on all these machines.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Machines with "<command>wins support = yes</command>" will keep a list of
|
||||
all NetBIOS names registered with them, acting as a DNS for NetBIOS names.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You should set up only ONE wins server. Do NOT set the
|
||||
"<command>wins support = yes</command>" option on more than one Samba
|
||||
server.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To set up a Windows NT Server as a WINS server you need to set up
|
||||
the WINS service - see your NT documentation for details. Note that
|
||||
Windows NT WINS Servers can replicate to each other, allowing more
|
||||
than one to be set up in a complex subnet environment. As Microsoft
|
||||
refuse to document these replication protocols Samba cannot currently
|
||||
participate in these replications. It is possible in the future that
|
||||
a Samba->Samba WINS replication protocol may be defined, in which
|
||||
case more than one Samba machine could be set up as a WINS server
|
||||
but currently only one Samba server should have the "wins support = yes"
|
||||
parameter set.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
After the WINS server has been configured you must ensure that all
|
||||
machines participating on the network are configured with the address
|
||||
of this WINS server. If your WINS server is a Samba machine, fill in
|
||||
the Samba machine IP address in the "Primary WINS Server" field of
|
||||
the "Control Panel->Network->Protocols->TCP->WINS Server" dialogs
|
||||
in Windows 95 or Windows NT. To tell a Samba server the IP address
|
||||
of the WINS server add the following line to the [global] section of
|
||||
all smb.conf files :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<command> wins server = >name or IP address<</command>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
where >name or IP address< is either the DNS name of the WINS server
|
||||
machine or its IP address.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that this line MUST NOT BE SET in the smb.conf file of the Samba
|
||||
server acting as the WINS server itself. If you set both the
|
||||
"<command>wins support = yes</command>" option and the
|
||||
"<command>wins server = >name<</command>" option then
|
||||
nmbd will fail to start.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There are two possible scenarios for setting up cross subnet browsing.
|
||||
The first details setting up cross subnet browsing on a network containing
|
||||
Windows 95, Samba and Windows NT machines that are not configured as
|
||||
part of a Windows NT Domain. The second details setting up cross subnet
|
||||
browsing on networks that contain NT Domains.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Setting up Browsing in a WORKGROUP</title>
|
||||
|
||||
<para>
|
||||
To set up cross subnet browsing on a network containing machines
|
||||
in up to be in a WORKGROUP, not an NT Domain you need to set up one
|
||||
Samba server to be the Domain Master Browser (note that this is *NOT*
|
||||
the same as a Primary Domain Controller, although in an NT Domain the
|
||||
same machine plays both roles). The role of a Domain master browser is
|
||||
to collate the browse lists from local master browsers on all the
|
||||
subnets that have a machine participating in the workgroup. Without
|
||||
one machine configured as a domain master browser each subnet would
|
||||
be an isolated workgroup, unable to see any machines on any other
|
||||
subnet. It is the presense of a domain master browser that makes
|
||||
cross subnet browsing possible for a workgroup.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In an WORKGROUP environment the domain master browser must be a
|
||||
Samba server, and there must only be one domain master browser per
|
||||
workgroup name. To set up a Samba server as a domain master browser,
|
||||
set the following option in the [global] section of the smb.conf file :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<command> domain master = yes</command>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The domain master browser should also preferrably be the local master
|
||||
browser for its own subnet. In order to achieve this set the following
|
||||
options in the [global] section of the smb.conf file :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
domain master = yes
|
||||
local master = yes
|
||||
preferred master = yes
|
||||
os level = 65
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The domain master browser may be the same machine as the WINS
|
||||
server, if you require.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Next, you should ensure that each of the subnets contains a
|
||||
machine that can act as a local master browser for the
|
||||
workgroup. Any NT machine should be able to do this, as will
|
||||
Windows 95 machines (although these tend to get rebooted more
|
||||
often, so it's not such a good idea to use these). To make a
|
||||
Samba server a local master browser set the following
|
||||
options in the [global] section of the smb.conf file :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
domain master = no
|
||||
local master = yes
|
||||
preferred master = yes
|
||||
os level = 65
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Do not do this for more than one Samba server on each subnet,
|
||||
or they will war with each other over which is to be the local
|
||||
master browser.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The "local master" parameter allows Samba to act as a local master
|
||||
browser. The "preferred master" causes nmbd to force a browser
|
||||
election on startup and the "os level" parameter sets Samba high
|
||||
enough so that it should win any browser elections.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you have an NT machine on the subnet that you wish to
|
||||
be the local master browser then you can disable Samba from
|
||||
becoming a local master browser by setting the following
|
||||
options in the [global] section of the smb.conf file :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
domain master = no
|
||||
local master = no
|
||||
preferred master = no
|
||||
os level = 0
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Setting up Browsing in a DOMAIN</title>
|
||||
|
||||
<para>
|
||||
If you are adding Samba servers to a Windows NT Domain then
|
||||
you must not set up a Samba server as a domain master browser.
|
||||
By default, a Windows NT Primary Domain Controller for a Domain
|
||||
name is also the Domain master browser for that name, and many
|
||||
things will break if a Samba server registers the Domain master
|
||||
browser NetBIOS name (DOMAIN>1B<) with WINS instead of the PDC.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For subnets other than the one containing the Windows NT PDC
|
||||
you may set up Samba servers as local master browsers as
|
||||
described. To make a Samba server a local master browser set
|
||||
the following options in the [global] section of the smb.conf
|
||||
file :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
domain master = no
|
||||
local master = yes
|
||||
preferred master = yes
|
||||
os level = 65
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you wish to have a Samba server fight the election with machines
|
||||
on the same subnet you may set the "os level" parameter to lower
|
||||
levels. By doing this you can tune the order of machines that
|
||||
will become local master browsers if they are running. For
|
||||
more details on this see the section "FORCING SAMBA TO BE THE MASTER"
|
||||
below.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you have Windows NT machines that are members of the domain
|
||||
on all subnets, and you are sure they will always be running then
|
||||
you can disable Samba from taking part in browser elections and
|
||||
ever becoming a local master browser by setting following options
|
||||
in the [global] section of the smb.conf file :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<command>
|
||||
domain master = no
|
||||
local master = no
|
||||
preferred master = no
|
||||
os level = 0
|
||||
</command>
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Forcing samba to be the master</title>
|
||||
|
||||
<para>
|
||||
Who becomes the "master browser" is determined by an election process
|
||||
using broadcasts. Each election packet contains a number of parameters
|
||||
which determine what precedence (bias) a host should have in the
|
||||
election. By default Samba uses a very low precedence and thus loses
|
||||
elections to just about anyone else.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you want Samba to win elections then just set the "os level" global
|
||||
option in smb.conf to a higher number. It defaults to 0. Using 34
|
||||
would make it win all elections over every other system (except other
|
||||
samba systems!)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A "os level" of 2 would make it beat WfWg and Win95, but not NTAS. A
|
||||
NTAS domain controller uses level 32.
|
||||
</para>
|
||||
|
||||
<para>The maximum os level is 255</para>
|
||||
|
||||
<para>
|
||||
If you want samba to force an election on startup, then set the
|
||||
"preferred master" global option in smb.conf to "yes". Samba will
|
||||
then have a slight advantage over other potential master browsers
|
||||
that are not preferred master browsers. Use this parameter with
|
||||
care, as if you have two hosts (whether they are windows 95 or NT or
|
||||
samba) on the same local subnet both set with "preferred master" to
|
||||
"yes", then periodically and continually they will force an election
|
||||
in order to become the local master browser.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you want samba to be a "domain master browser", then it is
|
||||
recommended that you also set "preferred master" to "yes", because
|
||||
samba will not become a domain master browser for the whole of your
|
||||
LAN or WAN if it is not also a local master browser on its own
|
||||
broadcast isolated subnet.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It is possible to configure two samba servers to attempt to become
|
||||
the domain master browser for a domain. The first server that comes
|
||||
up will be the domain master browser. All other samba servers will
|
||||
attempt to become the domain master browser every 5 minutes. They
|
||||
will find that another samba server is already the domain master
|
||||
browser and will fail. This provides automatic redundancy, should
|
||||
the current domain master browser fail.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Making samba the domain master</title>
|
||||
|
||||
<para>
|
||||
The domain master is responsible for collating the browse lists of
|
||||
multiple subnets so that browsing can occur between subnets. You can
|
||||
make samba act as the domain master by setting "domain master = yes"
|
||||
in smb.conf. By default it will not be a domain master.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that you should NOT set Samba to be the domain master for a
|
||||
workgroup that has the same name as an NT Domain.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When samba is the domain master and the master browser it will listen
|
||||
for master announcements (made roughly every twelve minutes) from local
|
||||
master browsers on other subnets and then contact them to synchronise
|
||||
browse lists.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you want samba to be the domain master then I suggest you also set
|
||||
the "os level" high enough to make sure it wins elections, and set
|
||||
"preferred master" to "yes", to get samba to force an election on
|
||||
startup.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that all your servers (including samba) and clients should be
|
||||
using a WINS server to resolve NetBIOS names. If your clients are only
|
||||
using broadcasting to resolve NetBIOS names, then two things will occur:
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
your local master browsers will be unable to find a domain master
|
||||
browser, as it will only be looking on the local subnet.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
if a client happens to get hold of a domain-wide browse list, and
|
||||
a user attempts to access a host in that list, it will be unable to
|
||||
resolve the NetBIOS name of that host.
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>
|
||||
If, however, both samba and your clients are using a WINS server, then:
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
your local master browsers will contact the WINS server and, as long as
|
||||
samba has registered that it is a domain master browser with the WINS
|
||||
server, your local master browser will receive samba's ip address
|
||||
as its domain master browser.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
when a client receives a domain-wide browse list, and a user attempts
|
||||
to access a host in that list, it will contact the WINS server to
|
||||
resolve the NetBIOS name of that host. as long as that host has
|
||||
registered its NetBIOS name with the same WINS server, the user will
|
||||
be able to see that host.
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Note about broadcast addresses</title>
|
||||
|
||||
<para>
|
||||
If your network uses a "0" based broadcast address (for example if it
|
||||
ends in a 0) then you will strike problems. Windows for Workgroups
|
||||
does not seem to support a 0's broadcast and you will probably find
|
||||
that browsing and name lookups won't work.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Multiple interfaces</title>
|
||||
|
||||
<para>
|
||||
Samba now supports machines with multiple network interfaces. If you
|
||||
have multiple interfaces then you will need to use the "interfaces"
|
||||
option in smb.conf to configure them. See smb.conf(5) for details.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
202
docs/docbook/projdoc/Bugs.sgml
Normal file
202
docs/docbook/projdoc/Bugs.sgml
Normal file
@ -0,0 +1,202 @@
|
||||
<chapter id="bugreport">
|
||||
|
||||
<chapterinfo>
|
||||
<author>
|
||||
<affiliation>
|
||||
<orgname>Samba Team</orgname>
|
||||
</affiliation>
|
||||
</author>
|
||||
<pubdate> 27 June 1997 </pubdate>
|
||||
</chapterinfo>
|
||||
|
||||
<title>Reporting Bugs</title>
|
||||
|
||||
<sect1>
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>
|
||||
The email address for bug reports is samba@samba.org
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Please take the time to read this file before you submit a bug
|
||||
report. Also, please see if it has changed between releases, as we
|
||||
may be changing the bug reporting mechanism at some time.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Please also do as much as you can yourself to help track down the
|
||||
bug. Samba is maintained by a dedicated group of people who volunteer
|
||||
their time, skills and efforts. We receive far more mail about it than
|
||||
we can possibly answer, so you have a much higher chance of an answer
|
||||
and a fix if you send us a "developer friendly" bug report that lets
|
||||
us fix it fast.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Do not assume that if you post the bug to the comp.protocols.smb
|
||||
newsgroup or the mailing list that we will read it. If you suspect that your
|
||||
problem is not a bug but a configuration problem then it is better to send
|
||||
it to the Samba mailing list, as there are (at last count) 5000 other users on
|
||||
that list that may be able to help you.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You may also like to look though the recent mailing list archives,
|
||||
which are conveniently accessible on the Samba web pages
|
||||
at http://samba.org/samba/
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>General info</title>
|
||||
|
||||
<para>
|
||||
Before submitting a bug report check your config for silly
|
||||
errors. Look in your log files for obvious messages that tell you that
|
||||
you've misconfigured something and run testparm to test your config
|
||||
file for correct syntax.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Have you run through the <ulink url="Diagnosis.html">diagnosis</ulink>?
|
||||
This is very important.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you include part of a log file with your bug report then be sure to
|
||||
annotate it with exactly what you were doing on the client at the
|
||||
time, and exactly what the results were.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Debug levels</title>
|
||||
|
||||
<para>
|
||||
If the bug has anything to do with Samba behaving incorrectly as a
|
||||
server (like refusing to open a file) then the log files will probably
|
||||
be very useful. Depending on the problem a log level of between 3 and
|
||||
10 showing the problem may be appropriate. A higher level givesmore
|
||||
detail, but may use too much disk space.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To set the debug level use <command>log level =</command> in your
|
||||
<filename>smb.conf</filename>. You may also find it useful to set the log
|
||||
level higher for just one machine and keep separate logs for each machine.
|
||||
To do this use:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
log level = 10
|
||||
log file = /usr/local/samba/lib/log.%m
|
||||
include = /usr/local/samba/lib/smb.conf.%m
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
then create a file
|
||||
<filename>/usr/local/samba/lib/smb.conf.machine</filename> where
|
||||
"machine" is the name of the client you wish to debug. In that file
|
||||
put any smb.conf commands you want, for example
|
||||
<command>log level=</command> may be useful. This also allows you to
|
||||
experiment with different security systems, protocol levels etc on just
|
||||
one machine.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <filename>smb.conf</filename> entry <command>log level =</command>
|
||||
is synonymous with the entry <command>debuglevel =</command> that has been
|
||||
used in older versions of Samba and is being retained for backwards
|
||||
compatibility of smb.conf files.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As the <command>log level =</command> value is increased you will record
|
||||
a significantly increasing level of debugging information. For most
|
||||
debugging operations you may not need a setting higher than 3. Nearly
|
||||
all bugs can be tracked at a setting of 10, but be prepared for a VERY
|
||||
large volume of log data.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Internal errors</title>
|
||||
|
||||
<para>
|
||||
If you get a "INTERNAL ERROR" message in your log files it means that
|
||||
Samba got an unexpected signal while running. It is probably a
|
||||
segmentation fault and almost certainly means a bug in Samba (unless
|
||||
you have faulty hardware or system software)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If the message came from smbd then it will probably be accompanied by
|
||||
a message which details the last SMB message received by smbd. This
|
||||
info is often very useful in tracking down the problem so please
|
||||
include it in your bug report.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You should also detail how to reproduce the problem, if
|
||||
possible. Please make this reasonably detailed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You may also find that a core file appeared in a "corefiles"
|
||||
subdirectory of the directory where you keep your samba log
|
||||
files. This file is the most useful tool for tracking down the bug. To
|
||||
use it you do this:
|
||||
</para>
|
||||
|
||||
<para><command>gdb smbd core</command></para>
|
||||
|
||||
<para>
|
||||
adding appropriate paths to smbd and core so gdb can find them. If you
|
||||
don't have gdb then try "dbx". Then within the debugger use the
|
||||
command "where" to give a stack trace of where the problem
|
||||
occurred. Include this in your mail.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you known any assembly language then do a "disass" of the routine
|
||||
where the problem occurred (if its in a library routine then
|
||||
disassemble the routine that called it) and try to work out exactly
|
||||
where the problem is by looking at the surrounding code. Even if you
|
||||
don't know assembly then incuding this info in the bug report can be
|
||||
useful.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Attaching to a running process</title>
|
||||
|
||||
<para>
|
||||
Unfortunately some unixes (in particular some recent linux kernels)
|
||||
refuse to dump a core file if the task has changed uid (which smbd
|
||||
does often). To debug with this sort of system you could try to attach
|
||||
to the running process using "gdb smbd PID" where you get PID from
|
||||
smbstatus. Then use "c" to continue and try to cause the core dump
|
||||
using the client. The debugger should catch the fault and tell you
|
||||
where it occurred.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Patches</title>
|
||||
|
||||
<para>
|
||||
The best sort of bug report is one that includes a fix! If you send us
|
||||
patches please use <command>diff -u</command> format if your version of
|
||||
diff supports it, otherwise use <command>diff -c4</command>. Make sure
|
||||
your do the diff against a clean version of the source and let me know
|
||||
exactly what version you used.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
509
docs/docbook/projdoc/Diagnosis.sgml
Normal file
509
docs/docbook/projdoc/Diagnosis.sgml
Normal file
@ -0,0 +1,509 @@
|
||||
<chapter id="diagnosis">
|
||||
<chapterinfo>
|
||||
<author>
|
||||
<firstname>Andrew</firstname><surname>Tridgell</surname>
|
||||
<affiliation>
|
||||
<orgname>Samba Team</orgname>
|
||||
<address><email>tridge@samba.org</email></address>
|
||||
</affiliation>
|
||||
</author>
|
||||
<pubdate> 1 November 1999</pubdate>
|
||||
</chapterinfo>
|
||||
|
||||
<title>Diagnosing your samba server</title>
|
||||
|
||||
<sect1>
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>
|
||||
This file contains a list of tests you can perform to validate your
|
||||
Samba server. It also tells you what the likely cause of the problem
|
||||
is if it fails any one of these steps. If it passes all these tests
|
||||
then it is probably working fine.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You should do ALL the tests, in the order shown. I have tried to
|
||||
carefully choose them so later tests only use capabilities verified in
|
||||
the earlier tests.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you send me an email saying "it doesn't work" and you have not
|
||||
followed this test procedure then you should not be surprised if I
|
||||
ignore your email.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Assumptions</title>
|
||||
|
||||
<para>
|
||||
In all of the tests I assume you have a Samba server called BIGSERVER
|
||||
and a PC called ACLIENT both in workgroup TESTGROUP. I also assume the
|
||||
PC is running windows for workgroups with a recent copy of the
|
||||
microsoft tcp/ip stack. Alternatively, your PC may be running Windows
|
||||
95 or Windows NT (Workstation or Server).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The procedure is similar for other types of clients.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
I also assume you know the name of an available share in your
|
||||
smb.conf. I will assume this share is called "tmp". You can add a
|
||||
"tmp" share like by adding the following to smb.conf:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
|
||||
[tmp]
|
||||
comment = temporary files
|
||||
path = /tmp
|
||||
read only = yes
|
||||
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
THESE TESTS ASSUME VERSION 2.0.6 OR LATER OF THE SAMBA SUITE. SOME
|
||||
COMMANDS SHOWN DID NOT EXIST IN EARLIER VERSIONS
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Please pay attention to the error messages you receive. If any error message
|
||||
reports that your server is being unfriendly you should first check that you
|
||||
IP name resolution is correctly set up. eg: Make sure your /etc/resolv.conf
|
||||
file points to name servers that really do exist.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Also, if you do not have DNS server access for name resolution please check
|
||||
that the settings for your smb.conf file results in "dns proxy = no". The
|
||||
best way to check this is with "testparm smb.conf"
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Tests</title>
|
||||
|
||||
<sect2>
|
||||
<title>Test 1</title>
|
||||
<para>
|
||||
In the directory in which you store your smb.conf file, run the command
|
||||
"testparm smb.conf". If it reports any errors then your smb.conf
|
||||
configuration file is faulty.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note: Your smb.conf file may be located in: <filename>/etc</filename>
|
||||
Or in: <filename>/usr/local/samba/lib</filename>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Test 2</title>
|
||||
|
||||
<para>
|
||||
Run the command "ping BIGSERVER" from the PC and "ping ACLIENT" from
|
||||
the unix box. If you don't get a valid response then your TCP/IP
|
||||
software is not correctly installed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that you will need to start a "dos prompt" window on the PC to
|
||||
run ping.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you get a message saying "host not found" or similar then your DNS
|
||||
software or /etc/hosts file is not correctly setup. It is possible to
|
||||
run samba without DNS entries for the server and client, but I assume
|
||||
you do have correct entries for the remainder of these tests.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Another reason why ping might fail is if your host is running firewall
|
||||
software. You will need to relax the rules to let in the workstation
|
||||
in question, perhaps by allowing access from another subnet (on Linux
|
||||
this is done via the ipfwadm program.)
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Test 3</title>
|
||||
|
||||
<para>
|
||||
Run the command "smbclient -L BIGSERVER" on the unix box. You
|
||||
should get a list of available shares back.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you get a error message containing the string "Bad password" then
|
||||
you probably have either an incorrect "hosts allow", "hosts deny" or
|
||||
"valid users" line in your smb.conf, or your guest account is not
|
||||
valid. Check what your guest account is using "testparm" and
|
||||
temporarily remove any "hosts allow", "hosts deny", "valid users" or
|
||||
"invalid users" lines.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you get a "connection refused" response then the smbd server may
|
||||
not be running. If you installed it in inetd.conf then you probably edited
|
||||
that file incorrectly. If you installed it as a daemon then check that
|
||||
it is running, and check that the netbios-ssn port is in a LISTEN
|
||||
state using "netstat -a".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you get a "session request failed" then the server refused the
|
||||
connection. If it says "Your server software is being unfriendly" then
|
||||
its probably because you have invalid command line parameters to smbd,
|
||||
or a similar fatal problem with the initial startup of smbd. Also
|
||||
check your config file (smb.conf) for syntax errors with "testparm"
|
||||
and that the various directories where samba keeps its log and lock
|
||||
files exist.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There are a number of reasons for which smbd may refuse or decline
|
||||
a session request. The most common of these involve one or more of
|
||||
the following smb.conf file entries:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
hosts deny = ALL
|
||||
hosts allow = xxx.xxx.xxx.xxx/yy
|
||||
bind interfaces only = Yes
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
In the above, no allowance has been made for any session requests that
|
||||
will automatically translate to the loopback adaptor address 127.0.0.1.
|
||||
To solve this problem change these lines to:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
hosts deny = ALL
|
||||
hosts allow = xxx.xxx.xxx.xxx/yy 127.
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
Do NOT use the "bind interfaces only" parameter where you may wish to
|
||||
use the samba password change facility, or where smbclient may need to
|
||||
access local service for name resolution or for local resource
|
||||
connections. (Note: the "bind interfaces only" parameter deficiency
|
||||
where it will not allow connections to the loopback address will be
|
||||
fixed soon).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Another common cause of these two errors is having something already running
|
||||
on port 139, such as Samba (ie: smbd is running from inetd already) or
|
||||
something like Digital's Pathworks. Check your inetd.conf file before trying
|
||||
to start smbd as a daemon, it can avoid a lot of frustration!
|
||||
</para>
|
||||
|
||||
<para>
|
||||
And yet another possible cause for failure of TEST 3 is when the subnet mask
|
||||
and / or broadcast address settings are incorrect. Please check that the
|
||||
network interface IP Address / Broadcast Address / Subnet Mask settings are
|
||||
correct and that Samba has correctly noted these in the log.nmb file.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Test 4</title>
|
||||
|
||||
<para>
|
||||
Run the command "nmblookup -B BIGSERVER __SAMBA__". You should get the
|
||||
IP address of your Samba server back.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you don't then nmbd is incorrectly installed. Check your inetd.conf
|
||||
if you run it from there, or that the daemon is running and listening
|
||||
to udp port 137.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
One common problem is that many inetd implementations can't take many
|
||||
parameters on the command line. If this is the case then create a
|
||||
one-line script that contains the right parameters and run that from
|
||||
inetd.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Test 5</title>
|
||||
|
||||
<para>run the command <command>nmblookup -B ACLIENT '*'</command></para>
|
||||
|
||||
<para>
|
||||
You should get the PCs IP address back. If you don't then the client
|
||||
software on the PC isn't installed correctly, or isn't started, or you
|
||||
got the name of the PC wrong.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If ACLIENT doesn't resolve via DNS then use the IP address of the
|
||||
client in the above test.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Test 6</title>
|
||||
|
||||
<para>
|
||||
Run the command <command>nmblookup -d 2 '*'</command>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This time we are trying the same as the previous test but are trying
|
||||
it via a broadcast to the default broadcast address. A number of
|
||||
Netbios/TCPIP hosts on the network should respond, although Samba may
|
||||
not catch all of the responses in the short time it listens. You
|
||||
should see "got a positive name query response" messages from several
|
||||
hosts.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If this doesn't give a similar result to the previous test then
|
||||
nmblookup isn't correctly getting your broadcast address through its
|
||||
automatic mechanism. In this case you should experiment use the
|
||||
"interfaces" option in smb.conf to manually configure your IP
|
||||
address, broadcast and netmask.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If your PC and server aren't on the same subnet then you will need to
|
||||
use the -B option to set the broadcast address to the that of the PCs
|
||||
subnet.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This test will probably fail if your subnet mask and broadcast address are
|
||||
not correct. (Refer to TEST 3 notes above).
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Test 7</title>
|
||||
|
||||
<para>
|
||||
Run the command <command>smbclient //BIGSERVER/TMP</command>. You should
|
||||
then be prompted for a password. You should use the password of the account
|
||||
you are logged into the unix box with. If you want to test with
|
||||
another account then add the -U >accountname< option to the end of
|
||||
the command line. eg:
|
||||
<command>smbclient //bigserver/tmp -Ujohndoe</command>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note: It is possible to specify the password along with the username
|
||||
as follows:
|
||||
<command>smbclient //bigserver/tmp -Ujohndoe%secret</command>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once you enter the password you should get the "smb>" prompt. If you
|
||||
don't then look at the error message. If it says "invalid network
|
||||
name" then the service "tmp" is not correctly setup in your smb.conf.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If it says "bad password" then the likely causes are:
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
you have shadow passords (or some other password system) but didn't
|
||||
compile in support for them in smbd
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
your "valid users" configuration is incorrect
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
you have a mixed case password and you haven't enabled the "password
|
||||
level" option at a high enough level
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
the "path =" line in smb.conf is incorrect. Check it with testparm
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
you enabled password encryption but didn't create the SMB encrypted
|
||||
password file
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>
|
||||
Once connected you should be able to use the commands
|
||||
<command>dir</command> <command>get</command> <command>put</command> etc.
|
||||
Type <command>help >command<</command> for instructions. You should
|
||||
especially check that the amount of free disk space shown is correct
|
||||
when you type <command>dir</command>.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Test 8</title>
|
||||
|
||||
<para>
|
||||
On the PC type the command <command>net view \\BIGSERVER</command>. You will
|
||||
need to do this from within a "dos prompt" window. You should get back a
|
||||
list of available shares on the server.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you get a "network name not found" or similar error then netbios
|
||||
name resolution is not working. This is usually caused by a problem in
|
||||
nmbd. To overcome it you could do one of the following (you only need
|
||||
to choose one of them):
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem><para>
|
||||
fixup the nmbd installation
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
add the IP address of BIGSERVER to the "wins server" box in the
|
||||
advanced tcp/ip setup on the PC.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
enable windows name resolution via DNS in the advanced section of
|
||||
the tcp/ip setup
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
add BIGSERVER to your lmhosts file on the PC.
|
||||
</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>
|
||||
If you get a "invalid network name" or "bad password error" then the
|
||||
same fixes apply as they did for the "smbclient -L" test above. In
|
||||
particular, make sure your "hosts allow" line is correct (see the man
|
||||
pages)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Also, do not overlook that fact that when the workstation requests the
|
||||
connection to the samba server it will attempt to connect using the
|
||||
name with which you logged onto your Windows machine. You need to make
|
||||
sure that an account exists on your Samba server with that exact same
|
||||
name and password.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you get "specified computer is not receiving requests" or similar
|
||||
it probably means that the host is not contactable via tcp services.
|
||||
Check to see if the host is running tcp wrappers, and if so add an entry in
|
||||
the hosts.allow file for your client (or subnet, etc.)
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Test 9</title>
|
||||
|
||||
<para>
|
||||
Run the command <command>net use x: \\BIGSERVER\TMP</command>. You should
|
||||
be prompted for a password then you should get a "command completed
|
||||
successfully" message. If not then your PC software is incorrectly
|
||||
installed or your smb.conf is incorrect. make sure your "hosts allow"
|
||||
and other config lines in smb.conf are correct.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It's also possible that the server can't work out what user name to
|
||||
connect you as. To see if this is the problem add the line "user =
|
||||
USERNAME" to the [tmp] section of smb.conf where "USERNAME" is the
|
||||
username corresponding to the password you typed. If you find this
|
||||
fixes things you may need the username mapping option.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Test 10</title>
|
||||
|
||||
<para>
|
||||
Run the command <command>nmblookup -M TESTGROUP</command> where
|
||||
TESTGROUP is the name of the workgroup that your Samba server and
|
||||
Windows PCs belong to. You should get back the IP address of the
|
||||
master browser for that workgroup.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you don't then the election process has failed. Wait a minute to
|
||||
see if it is just being slow then try again. If it still fails after
|
||||
that then look at the browsing options you have set in smb.conf. Make
|
||||
sure you have <command>preferred master = yes</command> to ensure that
|
||||
an election is held at startup.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Test 11</title>
|
||||
|
||||
<para>
|
||||
From file manager try to browse the server. Your samba server should
|
||||
appear in the browse list of your local workgroup (or the one you
|
||||
specified in smb.conf). You should be able to double click on the name
|
||||
of the server and get a list of shares. If you get a "invalid
|
||||
password" error when you do then you are probably running WinNT and it
|
||||
is refusing to browse a server that has no encrypted password
|
||||
capability and is in user level security mode. In this case either set
|
||||
<command>security = server</command> AND
|
||||
<command>password server = Windows_NT_Machine</command> in your
|
||||
smb.conf file, or enable encrypted passwords AFTER compiling in support
|
||||
for encrypted passwords (refer to the Makefile).
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Still having troubles?</title>
|
||||
|
||||
<para>
|
||||
Try the mailing list or newsgroup, or use the ethereal utility to
|
||||
sniff the problem. The official samba mailing list can be reached at
|
||||
<ulink url="mailto:samba@samba.org">samba@samba.org</ulink>. To find
|
||||
out more about samba and how to subscribe to the mailing list check
|
||||
out the samba web page at
|
||||
<ulink url="http://samba.org/samba">http://samba.org/samba</ulink>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Also look at the other docs in the Samba package!
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
398
docs/docbook/projdoc/Printing.sgml
Normal file
398
docs/docbook/projdoc/Printing.sgml
Normal file
@ -0,0 +1,398 @@
|
||||
<chapter id="printing_debug">
|
||||
<chapterinfo>
|
||||
<author>
|
||||
<firstname>Patrick</firstname><surname>Powell</surname>
|
||||
<affiliation>
|
||||
<address><email>papowell@lprng.org</email></address>
|
||||
</affiliation>
|
||||
</author>
|
||||
<pubdate>11 August 2000</pubdate>
|
||||
</chapterinfo>
|
||||
|
||||
<title>Debugging Printing Problems</title>
|
||||
|
||||
<sect1>
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>
|
||||
This is a short description of how to debug printing problems with
|
||||
Samba. This describes how to debug problems with printing from a SMB
|
||||
client to a Samba server, not the other way around. For the reverse
|
||||
see the examples/printing directory.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Ok, so you want to print to a Samba server from your PC. The first
|
||||
thing you need to understand is that Samba does not actually do any
|
||||
printing itself, it just acts as a middleman between your PC client
|
||||
and your Unix printing subsystem. Samba receives the file from the PC
|
||||
then passes the file to a external "print command". What print command
|
||||
you use is up to you.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The whole things is controlled using options in smb.conf. The most
|
||||
relevant options (which you should look up in the smb.conf man page)
|
||||
are:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
[global]
|
||||
print command - send a file to a spooler
|
||||
lpq command - get spool queue status
|
||||
lprm command - remove a job
|
||||
[printers]
|
||||
path = /var/spool/lpd/samba
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
The following are nice to know about:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
queuepause command - stop a printer or print queue
|
||||
queueresume command - start a printer or print queue
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
Example:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
print command = /usr/bin/lpr -r -P%p %s
|
||||
lpq command = /usr/bin/lpq -P%p %s
|
||||
lprm command = /usr/bin/lprm -P%p %j
|
||||
queuepause command = /usr/sbin/lpc -P%p stop
|
||||
queuepause command = /usr/sbin/lpc -P%p start
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
Samba should set reasonable defaults for these depending on your
|
||||
system type, but it isn't clairvoyant. It is not uncommon that you
|
||||
have to tweak these for local conditions. The commands should
|
||||
always have fully specified pathnames, as the smdb may not have
|
||||
the correct PATH values.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When you send a job to Samba to be printed, it will make a temporary
|
||||
copy of it in the directory specified in the [printers] section.
|
||||
and it should be periodically cleaned out. The lpr -r option
|
||||
requests that the temporary copy be removed after printing; If
|
||||
printing fails then you might find leftover files in this directory,
|
||||
and it should be periodically cleaned out. Samba used the lpq
|
||||
command to determine the "job number" assigned to your print job
|
||||
by the spooler.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The %>letter< are "macros" that get dynamically replaced with appropriate
|
||||
values when they are used. The %s gets replaced with the name of the spool
|
||||
file that Samba creates and the %p gets replaced with the name of the
|
||||
printer. The %j gets replaced with the "job number" which comes from
|
||||
the lpq output.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Debugging printer problems</title>
|
||||
|
||||
<para>
|
||||
One way to debug printing problems is to start by replacing these
|
||||
command with shell scripts that record the arguments and the contents
|
||||
of the print file. A simple example of this kind of things might
|
||||
be:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
print command = /tmp/saveprint %p %s
|
||||
|
||||
#!/bin/saveprint
|
||||
# we make sure that we are the right user
|
||||
/usr/bin/id -p >/tmp/tmp.print
|
||||
# we run the command and save the error messages
|
||||
# replace the command with the one appropriate for your system
|
||||
/usr/bin/lpr -r -P$1 $2 2>>&/tmp/tmp.print
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
Then you print a file and try removing it. You may find that the
|
||||
print queue needs to be stopped in order to see the queue status
|
||||
and remove the job:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
|
||||
h4: {42} % echo hi >/tmp/hi
|
||||
h4: {43} % smbclient //localhost/lw4
|
||||
added interface ip=10.0.0.4 bcast=10.0.0.255 nmask=255.255.255.0
|
||||
Password:
|
||||
Domain=[ASTART] OS=[Unix] Server=[Samba 2.0.7]
|
||||
smb: \> print /tmp/hi
|
||||
putting file /tmp/hi as hi-17534 (0.0 kb/s) (average 0.0 kb/s)
|
||||
smb: \> queue
|
||||
1049 3 hi-17534
|
||||
smb: \> cancel 1049
|
||||
Error cancelling job 1049 : code 0
|
||||
smb: \> cancel 1049
|
||||
Job 1049 cancelled
|
||||
smb: \> queue
|
||||
smb: \> exit
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
The 'code 0' indicates that the job was removed. The comment
|
||||
by the smbclient is a bit misleading on this.
|
||||
You can observe the command output and then and look at the
|
||||
/tmp/tmp.print file to see what the results are. You can quickly
|
||||
find out if the problem is with your printing system. Often people
|
||||
have problems with their /etc/printcap file or permissions on
|
||||
various print queues.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>What printers do I have?</title>
|
||||
|
||||
<para>
|
||||
You can use the 'testprns' program to check to see if the printer
|
||||
name you are using is recognized by Samba. For example, you can
|
||||
use:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
testprns printer /etc/printcap
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
Samba can get its printcap information from a file or from a program.
|
||||
You can try the following to see the format of the extracted
|
||||
information:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
testprns -a printer /etc/printcap
|
||||
|
||||
testprns -a printer '|/bin/cat printcap'
|
||||
</programlisting></para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Setting up printcap and print servers</title>
|
||||
|
||||
<para>
|
||||
You may need to set up some printcaps for your Samba system to use.
|
||||
It is strongly recommended that you use the facilities provided by
|
||||
the print spooler to set up queues and printcap information.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Samba requires either a printcap or program to deliver printcap
|
||||
information. This printcap information has the format:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
name|alias1|alias2...:option=value:...
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
For almost all printing systems, the printer 'name' must be composed
|
||||
only of alphanumeric or underscore '_' characters. Some systems also
|
||||
allow hyphens ('-') as well. An alias is an alternative name for the
|
||||
printer, and an alias with a space in it is used as a 'comment'
|
||||
about the printer. The printcap format optionally uses a \ at the end of lines
|
||||
to extend the printcap to multiple lines.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Here are some examples of printcap files:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<orderedlist>
|
||||
<listitem><para>
|
||||
pr just printer name
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
pr|alias printer name and alias
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
pr|My Printer printer name, alias used as comment
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
pr:sh:\ Same as pr:sh:cm= testing
|
||||
:cm= \
|
||||
testing
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
pr:sh Same as pr:sh:cm= testing
|
||||
:cm= testing
|
||||
</para></listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Samba reads the printcap information when first started. If you make
|
||||
changes in the printcap information, then you must do the following:
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
|
||||
<listitem><para>
|
||||
make sure that the print spooler is aware of these changes.
|
||||
The LPRng system uses the 'lpc reread' command to do this.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
make sure that the spool queues, etc., exist and have the
|
||||
correct permissions. The LPRng system uses the 'checkpc -f'
|
||||
command to do this.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
You now should send a SIGHUP signal to the smbd server to have
|
||||
it reread the printcap information.
|
||||
</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Job sent, no output</title>
|
||||
|
||||
<para>
|
||||
This is the most frustrating part of printing. You may have sent the
|
||||
job, verified that the job was forwarded, set up a wrapper around
|
||||
the command to send the file, but there was no output from the printer.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
First, check to make sure that the job REALLY is getting to the
|
||||
right print queue. If you are using a BSD or LPRng print spooler,
|
||||
you can temporarily stop the printing of jobs. Jobs can still be
|
||||
submitted, but they will not be printed. Use:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
lpc -Pprinter stop
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
Now submit a print job and then use 'lpq -Pprinter' to see if the
|
||||
job is in the print queue. If it is not in the print queue then
|
||||
you will have to find out why it is not being accepted for printing.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Next, you may want to check to see what the format of the job really
|
||||
was. With the assistance of the system administrator you can view
|
||||
the submitted jobs files. You may be surprised to find that these
|
||||
are not in what you would expect to call a printable format.
|
||||
You can use the UNIX 'file' utitily to determine what the job
|
||||
format actually is:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
cd /var/spool/lpd/printer # spool directory of print jobs
|
||||
ls # find job files
|
||||
file dfA001myhost
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
You should make sure that your printer supports this format OR that
|
||||
your system administrator has installed a 'print filter' that will
|
||||
convert the file to a format appropriate for your printer.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Job sent, strange output</title>
|
||||
|
||||
<para>
|
||||
Once you have the job printing, you can then start worrying about
|
||||
making it print nicely.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The most common problem is extra pages of output: banner pages
|
||||
OR blank pages at the end.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you are getting banner pages, check and make sure that the
|
||||
printcap option or printer option is configured for no banners.
|
||||
If you have a printcap, this is the :sh (suppress header or banner
|
||||
page) option. You should have the following in your printer.
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
printer: ... :sh
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
If you have this option and are still getting banner pages, there
|
||||
is a strong chance that your printer is generating them for you
|
||||
automatically. You should make sure that banner printing is disabled
|
||||
for the printer. This usually requires using the printer setup software
|
||||
or procedures supplied by the printer manufacturer.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you get an extra page of output, this could be due to problems
|
||||
with your job format, or if you are generating PostScript jobs,
|
||||
incorrect setting on your printer driver on the MicroSoft client.
|
||||
For example, under Win95 there is a option:
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
Printers|Printer Name|(Right Click)Properties|Postscript|Advanced|
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
that allows you to choose if a Ctrl-D is appended to all jobs.
|
||||
This is a very bad thing to do, as most spooling systems will
|
||||
automatically add a ^D to the end of the job if it is detected as
|
||||
PostScript. The multiple ^D may cause an additional page of output.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Raw PostScript printed</title>
|
||||
|
||||
<para>
|
||||
This is a problem that is usually caused by either the print spooling
|
||||
system putting information at the start of the print job that makes
|
||||
the printer think the job is a text file, or your printer simply
|
||||
does not support PostScript. You may need to enable 'Automatic
|
||||
Format Detection' on your printer.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Advanced Printing</title>
|
||||
|
||||
<para>
|
||||
Note that you can do some pretty magic things by using your
|
||||
imagination with the "print command" option and some shell scripts.
|
||||
Doing print accounting is easy by passing the %U option to a print
|
||||
command shell script. You could even make the print command detect
|
||||
the type of output and its size and send it to an appropriate
|
||||
printer.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Real debugging</title>
|
||||
|
||||
<para>
|
||||
If the above debug tips don't help, then maybe you need to bring in
|
||||
the bug guns, system tracing. See Tracing.txt in this directory.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
578
docs/docbook/projdoc/Speed.sgml
Normal file
578
docs/docbook/projdoc/Speed.sgml
Normal file
@ -0,0 +1,578 @@
|
||||
<chapter id="speed">
|
||||
|
||||
<chapterinfo>
|
||||
<author>
|
||||
<affiliation>
|
||||
<orgname>Samba Team</orgname>
|
||||
<address><email>samba@samba.org</email></address>
|
||||
</affiliation>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Paul</firstname><surname>Cochrane</surname>
|
||||
<affiliation>
|
||||
<orgname>Dundee Limb Fitting Centre</orgname>
|
||||
<address><email>paulc@dth.scot.nhs.uk</email></address>
|
||||
</affiliation>
|
||||
</author>
|
||||
</chapterinfo>
|
||||
|
||||
<title>Samba performance issues</title>
|
||||
|
||||
<sect1>
|
||||
<title>Comparisons</title>
|
||||
|
||||
<para>
|
||||
The Samba server uses TCP to talk to the client. Thus if you are
|
||||
trying to see if it performs well you should really compare it to
|
||||
programs that use the same protocol. The most readily available
|
||||
programs for file transfer that use TCP are ftp or another TCP based
|
||||
SMB server.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you want to test against something like a NT or WfWg server then
|
||||
you will have to disable all but TCP on either the client or
|
||||
server. Otherwise you may well be using a totally different protocol
|
||||
(such as Netbeui) and comparisons may not be valid.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Generally you should find that Samba performs similarly to ftp at raw
|
||||
transfer speed. It should perform quite a bit faster than NFS,
|
||||
although this very much depends on your system.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Several people have done comparisons between Samba and Novell, NFS or
|
||||
WinNT. In some cases Samba performed the best, in others the worst. I
|
||||
suspect the biggest factor is not Samba vs some other system but the
|
||||
hardware and drivers used on the various systems. Given similar
|
||||
hardware Samba should certainly be competitive in speed with other
|
||||
systems.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Oplocks</title>
|
||||
|
||||
<sect2>
|
||||
<title>Overview</title>
|
||||
|
||||
<para>
|
||||
Oplocks are the way that SMB clients get permission from a server to
|
||||
locally cache file operations. If a server grants an oplock
|
||||
(opportunistic lock) then the client is free to assume that it is the
|
||||
only one accessing the file and it will agressively cache file
|
||||
data. With some oplock types the client may even cache file open/close
|
||||
operations. This can give enormous performance benefits.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
With the release of Samba 1.9.18 we now correctly support opportunistic
|
||||
locks. This is turned on by default, and can be turned off on a share-
|
||||
by-share basis by setting the parameter :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<command>oplocks = False</command>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
We recommend that you leave oplocks on however, as current benchmark
|
||||
tests with NetBench seem to give approximately a 30% improvement in
|
||||
speed with them on. This is on average however, and the actual
|
||||
improvement seen can be orders of magnitude greater, depending on
|
||||
what the client redirector is doing.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Previous to Samba 1.9.18 there was a 'fake oplocks' option. This
|
||||
option has been left in the code for backwards compatibility reasons
|
||||
but it's use is now deprecated. A short summary of what the old
|
||||
code did follows.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Level2 Oplocks</title>
|
||||
|
||||
<para>
|
||||
With Samba 2.0.5 a new capability - level2 (read only) oplocks is
|
||||
supported (although the option is off by default - see the smb.conf
|
||||
man page for details). Turning on level2 oplocks (on a share-by-share basis)
|
||||
by setting the parameter :
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<command>level2 oplocks = true</command>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
should speed concurrent access to files that are not commonly written
|
||||
to, such as application serving shares (ie. shares that contain common
|
||||
.EXE files - such as a Microsoft Office share) as it allows clients to
|
||||
read-ahread cache copies of these files.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Old 'fake oplocks' option - deprecated</title>
|
||||
|
||||
<para>
|
||||
Samba can also fake oplocks, by granting a oplock whenever a client
|
||||
asks for one. This is controlled using the smb.conf option "fake
|
||||
oplocks". If you set "fake oplocks = yes" then you are telling the
|
||||
client that it may agressively cache the file data for all opens.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Enabling 'fake oplocks' on all read-only shares or shares that you know
|
||||
will only be accessed from one client at a time you will see a big
|
||||
performance improvement on many operations. If you enable this option
|
||||
on shares where multiple clients may be accessing the files read-write
|
||||
at the same time you can get data corruption.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Socket options</title>
|
||||
|
||||
<para>
|
||||
There are a number of socket options that can greatly affect the
|
||||
performance of a TCP based server like Samba.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The socket options that Samba uses are settable both on the command
|
||||
line with the -O option, or in the smb.conf file.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The "socket options" section of the smb.conf manual page describes how
|
||||
to set these and gives recommendations.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Getting the socket options right can make a big difference to your
|
||||
performance, but getting them wrong can degrade it by just as
|
||||
much. The correct settings are very dependent on your local network.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The socket option TCP_NODELAY is the one that seems to make the
|
||||
biggest single difference for most networks. Many people report that
|
||||
adding "socket options = TCP_NODELAY" doubles the read performance of
|
||||
a Samba drive. The best explanation I have seen for this is that the
|
||||
Microsoft TCP/IP stack is slow in sending tcp ACKs.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Read size</title>
|
||||
|
||||
<para>
|
||||
The option "read size" affects the overlap of disk reads/writes with
|
||||
network reads/writes. If the amount of data being transferred in
|
||||
several of the SMB commands (currently SMBwrite, SMBwriteX and
|
||||
SMBreadbraw) is larger than this value then the server begins writing
|
||||
the data before it has received the whole packet from the network, or
|
||||
in the case of SMBreadbraw, it begins writing to the network before
|
||||
all the data has been read from disk.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This overlapping works best when the speeds of disk and network access
|
||||
are similar, having very little effect when the speed of one is much
|
||||
greater than the other.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The default value is 16384, but very little experimentation has been
|
||||
done yet to determine the optimal value, and it is likely that the best
|
||||
value will vary greatly between systems anyway. A value over 65536 is
|
||||
pointless and will cause you to allocate memory unnecessarily.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Max xmit</title>
|
||||
|
||||
<para>
|
||||
At startup the client and server negotiate a "maximum transmit" size,
|
||||
which limits the size of nearly all SMB commands. You can set the
|
||||
maximum size that Samba will negotiate using the "max xmit = " option
|
||||
in smb.conf. Note that this is the maximum size of SMB request that
|
||||
Samba will accept, but not the maximum size that the *client* will accept.
|
||||
The client maximum receive size is sent to Samba by the client and Samba
|
||||
honours this limit.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It defaults to 65536 bytes (the maximum), but it is possible that some
|
||||
clients may perform better with a smaller transmit unit. Trying values
|
||||
of less than 2048 is likely to cause severe problems.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In most cases the default is the best option.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Locking</title>
|
||||
|
||||
<para>
|
||||
By default Samba does not implement strict locking on each read/write
|
||||
call (although it did in previous versions). If you enable strict
|
||||
locking (using "strict locking = yes") then you may find that you
|
||||
suffer a severe performance hit on some systems.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The performance hit will probably be greater on NFS mounted
|
||||
filesystems, but could be quite high even on local disks.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Share modes</title>
|
||||
|
||||
<para>
|
||||
Some people find that opening files is very slow. This is often
|
||||
because of the "share modes" code needed to fully implement the dos
|
||||
share modes stuff. You can disable this code using "share modes =
|
||||
no". This will gain you a lot in opening and closing files but will
|
||||
mean that (in some cases) the system won't force a second user of a
|
||||
file to open the file read-only if the first has it open
|
||||
read-write. For many applications that do their own locking this
|
||||
doesn't matter, but for some it may. Most Windows applications
|
||||
depend heavily on "share modes" working correctly and it is
|
||||
recommended that the Samba share mode support be left at the
|
||||
default of "on".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The share mode code in Samba has been re-written in the 1.9.17
|
||||
release following tests with the Ziff-Davis NetBench PC Benchmarking
|
||||
tool. It is now believed that Samba 1.9.17 implements share modes
|
||||
similarly to Windows NT.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
NOTE: In the most recent versions of Samba there is an option to use
|
||||
shared memory via mmap() to implement the share modes. This makes
|
||||
things much faster. See the Makefile for how to enable this.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Log level</title>
|
||||
|
||||
<para>
|
||||
If you set the log level (also known as "debug level") higher than 2
|
||||
then you may suffer a large drop in performance. This is because the
|
||||
server flushes the log file after each operation, which can be very
|
||||
expensive.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Wide lines</title>
|
||||
|
||||
<para>
|
||||
The "wide links" option is now enabled by default, but if you disable
|
||||
it (for better security) then you may suffer a performance hit in
|
||||
resolving filenames. The performance loss is lessened if you have
|
||||
"getwd cache = yes", which is now the default.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Read raw</title>
|
||||
|
||||
<para>
|
||||
The "read raw" operation is designed to be an optimised, low-latency
|
||||
file read operation. A server may choose to not support it,
|
||||
however. and Samba makes support for "read raw" optional, with it
|
||||
being enabled by default.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In some cases clients don't handle "read raw" very well and actually
|
||||
get lower performance using it than they get using the conventional
|
||||
read operations.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
So you might like to try "read raw = no" and see what happens on your
|
||||
network. It might lower, raise or not affect your performance. Only
|
||||
testing can really tell.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Write raw</title>
|
||||
|
||||
<para>
|
||||
The "write raw" operation is designed to be an optimised, low-latency
|
||||
file write operation. A server may choose to not support it,
|
||||
however. and Samba makes support for "write raw" optional, with it
|
||||
being enabled by default.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Some machines may find "write raw" slower than normal write, in which
|
||||
case you may wish to change this option.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Read prediction</title>
|
||||
|
||||
<para>
|
||||
Samba can do read prediction on some of the SMB commands. Read
|
||||
prediction means that Samba reads some extra data on the last file it
|
||||
read while waiting for the next SMB command to arrive. It can then
|
||||
respond more quickly when the next read request arrives.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This is disabled by default. You can enable it by using "read
|
||||
prediction = yes".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that read prediction is only used on files that were opened read
|
||||
only.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Read prediction should particularly help for those silly clients (such
|
||||
as "Write" under NT) which do lots of very small reads on a file.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Samba will not read ahead more data than the amount specified in the
|
||||
"read size" option. It always reads ahead on 1k block boundaries.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Memory mapping</title>
|
||||
|
||||
<para>
|
||||
Samba supports reading files via memory mapping them. One some
|
||||
machines this can give a large boost to performance, on others it
|
||||
makes not difference at all, and on some it may reduce performance.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To enable you you have to recompile Samba with the -DUSE_MMAP option
|
||||
on the FLAGS line of the Makefile.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that memory mapping is only used on files opened read only, and
|
||||
is not used by the "read raw" operation. Thus you may find memory
|
||||
mapping is more effective if you disable "read raw" using "read raw =
|
||||
no".
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Slow Clients</title>
|
||||
|
||||
<para>
|
||||
One person has reported that setting the protocol to COREPLUS rather
|
||||
than LANMAN2 gave a dramatic speed improvement (from 10k/s to 150k/s).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
I suspect that his PC's (386sx16 based) were asking for more data than
|
||||
they could chew. I suspect a similar speed could be had by setting
|
||||
"read raw = no" and "max xmit = 2048", instead of changing the
|
||||
protocol. Lowering the "read size" might also help.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Slow Logins</title>
|
||||
|
||||
<para>
|
||||
Slow logins are almost always due to the password checking time. Using
|
||||
the lowest practical "password level" will improve things a lot. You
|
||||
could also enable the "UFC crypt" option in the Makefile.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Client tuning</title>
|
||||
|
||||
<para>
|
||||
Often a speed problem can be traced to the client. The client (for
|
||||
example Windows for Workgroups) can often be tuned for better TCP
|
||||
performance.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
See your client docs for details. In particular, I have heard rumours
|
||||
that the WfWg options TCPWINDOWSIZE and TCPSEGMENTSIZE can have a
|
||||
large impact on performance.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Also note that some people have found that setting DefaultRcvWindow in
|
||||
the [MSTCP] section of the SYSTEM.INI file under WfWg to 3072 gives a
|
||||
big improvement. I don't know why.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
My own experience wth DefaultRcvWindow is that I get much better
|
||||
performance with a large value (16384 or larger). Other people have
|
||||
reported that anything over 3072 slows things down enourmously. One
|
||||
person even reported a speed drop of a factor of 30 when he went from
|
||||
3072 to 8192. I don't know why.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It probably depends a lot on your hardware, and the type of unix box
|
||||
you have at the other end of the link.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Paul Cochrane has done some testing on client side tuning and come
|
||||
to the following conclusions:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Install the W2setup.exe file from www.microsoft.com. This is an
|
||||
update for the winsock stack and utilities which improve performance.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Configure the win95 TCPIP registry settings to give better
|
||||
perfomance. I use a program called MTUSPEED.exe which I got off the
|
||||
net. There are various other utilities of this type freely available.
|
||||
The setting which give the best performance for me are:
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem><para>
|
||||
MaxMTU Remove
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
RWIN Remove
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
MTUAutoDiscover Disable
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
MTUBlackHoleDetect Disable
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
Time To Live Enabled
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
Time To Live - HOPS 32
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
NDI Cache Size 0
|
||||
</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>
|
||||
I tried virtually all of the items mentioned in the document and
|
||||
the only one which made a difference to me was the socket options. It
|
||||
turned out I was better off without any!!!!!
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In terms of overall speed of transfer, between various win95 clients
|
||||
and a DX2-66 20MB server with a crappy NE2000 compatible and old IDE
|
||||
drive (Kernel 2.0.30). The transfer rate was reasonable for 10 baseT.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
FIXME
|
||||
The figures are: Put Get
|
||||
P166 client 3Com card: 420-440kB/s 500-520kB/s
|
||||
P100 client 3Com card: 390-410kB/s 490-510kB/s
|
||||
DX4-75 client NE2000: 370-380kB/s 330-350kB/s
|
||||
</para>
|
||||
|
||||
<para>
|
||||
I based these test on transfer two files a 4.5MB text file and a 15MB
|
||||
textfile. The results arn't bad considering the hardware Samba is
|
||||
running on. It's a crap machine!!!!
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The updates mentioned in 1 and 2 brought up the transfer rates from
|
||||
just over 100kB/s in some clients.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A new client is a P333 connected via a 100MB/s card and hub. The
|
||||
transfer rates from this were good: 450-500kB/s on put and 600+kB/s
|
||||
on get.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Looking at standard FTP throughput, Samba is a bit slower (100kB/s
|
||||
upwards). I suppose there is more going on in the samba protocol, but
|
||||
if it could get up to the rate of FTP the perfomance would be quite
|
||||
staggering.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>My Results</title>
|
||||
|
||||
<para>
|
||||
Some people want to see real numbers in a document like this, so here
|
||||
they are. I have a 486sx33 client running WfWg 3.11 with the 3.11b
|
||||
tcp/ip stack. It has a slow IDE drive and 20Mb of ram. It has a SMC
|
||||
Elite-16 ISA bus ethernet card. The only WfWg tuning I've done is to
|
||||
set DefaultRcvWindow in the [MSTCP] section of system.ini to 16384. My
|
||||
server is a 486dx3-66 running Linux. It also has 20Mb of ram and a SMC
|
||||
Elite-16 card. You can see my server config in the examples/tridge/
|
||||
subdirectory of the distribution.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
I get 490k/s on reading a 8Mb file with copy.
|
||||
I get 441k/s writing the same file to the samba server.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Of course, there's a lot more to benchmarks than 2 raw throughput
|
||||
figures, but it gives you a ballpark figure.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
I've also tested Win95 and WinNT, and found WinNT gave me the best
|
||||
speed as a samba client. The fastest client of all (for me) is
|
||||
smbclient running on another linux box. Maybe I'll add those results
|
||||
here someday ...
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
</chapter>
|
140
docs/docbook/projdoc/security_level.sgml
Normal file
140
docs/docbook/projdoc/security_level.sgml
Normal file
@ -0,0 +1,140 @@
|
||||
<chapter id="security_levels">
|
||||
<chapterinfo>
|
||||
<author>
|
||||
<firstname>Andrew</firstname><surname>Tridgell</surname>
|
||||
<affiliation>
|
||||
<orgname>Samba Team</orgname>
|
||||
<address><email>samba@samba.org</email></address>
|
||||
</affiliation>
|
||||
</author>
|
||||
</chapterinfo>
|
||||
|
||||
<title>Security levels</title>
|
||||
|
||||
<sect1>
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>
|
||||
Samba supports the following options to the global smb.conf parameter
|
||||
</para>
|
||||
|
||||
<para><programlisting>
|
||||
[global]
|
||||
<ulink url="smb.conf.5.html#SECURITY"><parameter>security</parameter></ulink> = [share|user(default)|domain|ads]
|
||||
</programlisting></para>
|
||||
|
||||
<para>
|
||||
Please refer to the smb.conf man page for usage information and to the document
|
||||
<ulink url="DOMAIN_MEMBER.html">DOMAIN_MEMBER.html</ulink> for further background details
|
||||
on domain mode security. The Windows 2000 Kerberos domain security model
|
||||
(security = ads) is described in the <ulink url="ADS-HOWTO.html">ADS-HOWTO.html</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Of the above, "security = server" means that Samba reports to clients that
|
||||
it is running in "user mode" but actually passes off all authentication
|
||||
requests to another "user mode" server. This requires an additional
|
||||
parameter "password server =" that points to the real authentication server.
|
||||
That real authentication server can be another Samba server or can be a
|
||||
Windows NT server, the later natively capable of encrypted password support.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>More complete description of security levels</title>
|
||||
|
||||
<para>
|
||||
A SMB server tells the client at startup what "security level" it is
|
||||
running. There are two options "share level" and "user level". Which
|
||||
of these two the client receives affects the way the client then tries
|
||||
to authenticate itself. It does not directly affect (to any great
|
||||
extent) the way the Samba server does security. I know this is
|
||||
strange, but it fits in with the client/server approach of SMB. In SMB
|
||||
everything is initiated and controlled by the client, and the server
|
||||
can only tell the client what is available and whether an action is
|
||||
allowed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
I'll describe user level security first, as its simpler. In user level
|
||||
security the client will send a "session setup" command directly after
|
||||
the protocol negotiation. This contains a username and password. The
|
||||
server can either accept or reject that username/password
|
||||
combination. Note that at this stage the server has no idea what
|
||||
share the client will eventually try to connect to, so it can't base
|
||||
the "accept/reject" on anything other than:
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem><para>the username/password</para></listitem>
|
||||
<listitem><para>the machine that the client is coming from</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>
|
||||
If the server accepts the username/password then the client expects to
|
||||
be able to mount any share (using a "tree connection") without
|
||||
specifying a password. It expects that all access rights will be as
|
||||
the username/password specified in the "session setup".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It is also possible for a client to send multiple "session setup"
|
||||
requests. When the server responds it gives the client a "uid" to use
|
||||
as an authentication tag for that username/password. The client can
|
||||
maintain multiple authentication contexts in this way (WinDD is an
|
||||
example of an application that does this)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Ok, now for share level security. In share level security the client
|
||||
authenticates itself separately for each share. It will send a
|
||||
password along with each "tree connection" (share mount). It does not
|
||||
explicitly send a username with this operation. The client is
|
||||
expecting a password to be associated with each share, independent of
|
||||
the user. This means that samba has to work out what username the
|
||||
client probably wants to use. It is never explicitly sent the
|
||||
username. Some commercial SMB servers such as NT actually associate
|
||||
passwords directly with shares in share level security, but samba
|
||||
always uses the unix authentication scheme where it is a
|
||||
username/password that is authenticated, not a "share/password".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Many clients send a "session setup" even if the server is in share
|
||||
level security. They normally send a valid username but no
|
||||
password. Samba records this username in a list of "possible
|
||||
usernames". When the client then does a "tree connection" it also adds
|
||||
to this list the name of the share they try to connect to (useful for
|
||||
home directories) and any users listed in the "user =" smb.conf
|
||||
line. The password is then checked in turn against these "possible
|
||||
usernames". If a match is found then the client is authenticated as
|
||||
that user.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Finally "server level" security. In server level security the samba
|
||||
server reports to the client that it is in user level security. The
|
||||
client then does a "session setup" as described earlier. The samba
|
||||
server takes the username/password that the client sends and attempts
|
||||
to login to the "password server" by sending exactly the same
|
||||
username/password that it got from the client. If that server is in
|
||||
user level security and accepts the password then samba accepts the
|
||||
clients connection. This allows the samba server to use another SMB
|
||||
server as the "password server".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You should also note that at the very start of all this, where the
|
||||
server tells the client what security level it is in, it also tells
|
||||
the client if it supports encryption. If it does then it supplies the
|
||||
client with a random "cryptkey". The client will then send all
|
||||
passwords in encrypted form. You have to compile samba with encryption
|
||||
enabled to support this feature, and you have to maintain a separate
|
||||
smbpasswd file with SMB style encrypted passwords. It is
|
||||
cryptographically impossible to translate from unix style encryption
|
||||
to SMB style encryption, although there are some fairly simple management
|
||||
schemes by which the two could be kept in sync.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
741
docs/htmldocs/Browsing.html
Normal file
741
docs/htmldocs/Browsing.html
Normal file
@ -0,0 +1,741 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Improved browsing in samba</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD
|
||||
><BODY
|
||||
CLASS="ARTICLE"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="ARTICLE"
|
||||
><DIV
|
||||
CLASS="TITLEPAGE"
|
||||
><H1
|
||||
CLASS="TITLE"
|
||||
><A
|
||||
NAME="IMPROVED-BROWSING"
|
||||
>Improved browsing in samba</A
|
||||
></H1
|
||||
><HR></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN3"
|
||||
>Overview of browsing</A
|
||||
></H1
|
||||
><P
|
||||
>SMB networking provides a mechanism by which clients can access a list
|
||||
of machines in a network, a so-called "browse list". This list
|
||||
contains machines that are ready to offer file and/or print services
|
||||
to other machines within the network. Thus it does not include
|
||||
machines which aren't currently able to do server tasks. The browse
|
||||
list is heavily used by all SMB clients. Configuration of SMB
|
||||
browsing has been problematic for some Samba users, hence this
|
||||
document.</P
|
||||
><P
|
||||
>Browsing will NOT work if name resolution from NetBIOS names to IP
|
||||
addresses does not function correctly. Use of a WINS server is highly
|
||||
recommended to aid the resolution of NetBIOS (SMB) names to IP addresses.
|
||||
WINS allows remote segment clients to obtain NetBIOS name_type information
|
||||
that can NOT be provided by any other means of name resolution.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN7"
|
||||
>Browsing support in samba</A
|
||||
></H1
|
||||
><P
|
||||
>Samba now fully supports browsing. The browsing is supported by nmbd
|
||||
and is also controlled by options in the smb.conf file (see smb.conf(5)).</P
|
||||
><P
|
||||
>Samba can act as a local browse master for a workgroup and the ability
|
||||
for samba to support domain logons and scripts is now available. See
|
||||
DOMAIN.txt for more information on domain logons.</P
|
||||
><P
|
||||
>Samba can also act as a domain master browser for a workgroup. This
|
||||
means that it will collate lists from local browse masters into a
|
||||
wide area network server list. In order for browse clients to
|
||||
resolve the names they may find in this list, it is recommended that
|
||||
both samba and your clients use a WINS server.</P
|
||||
><P
|
||||
>Note that you should NOT set Samba to be the domain master for a
|
||||
workgroup that has the same name as an NT Domain: on each wide area
|
||||
network, you must only ever have one domain master browser per workgroup,
|
||||
regardless of whether it is NT, Samba or any other type of domain master
|
||||
that is providing this service.</P
|
||||
><P
|
||||
>[Note that nmbd can be configured as a WINS server, but it is not
|
||||
necessary to specifically use samba as your WINS server. NTAS can
|
||||
be configured as your WINS server. In a mixed NT server and
|
||||
samba environment on a Wide Area Network, it is recommended that
|
||||
you use the NT server's WINS server capabilities. In a samba-only
|
||||
environment, it is recommended that you use one and only one nmbd
|
||||
as your WINS server].</P
|
||||
><P
|
||||
>To get browsing to work you need to run nmbd as usual, but will need
|
||||
to use the "workgroup" option in smb.conf to control what workgroup
|
||||
Samba becomes a part of.</P
|
||||
><P
|
||||
>Samba also has a useful option for a Samba server to offer itself for
|
||||
browsing on another subnet. It is recommended that this option is only
|
||||
used for 'unusual' purposes: announcements over the internet, for
|
||||
example. See "remote announce" in the smb.conf man page. </P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN16"
|
||||
>Problem resolution</A
|
||||
></H1
|
||||
><P
|
||||
>If something doesn't work then hopefully the log.nmb file will help
|
||||
you track down the problem. Try a debug level of 2 or 3 for finding
|
||||
problems. Also note that the current browse list usually gets stored
|
||||
in text form in a file called browse.dat.</P
|
||||
><P
|
||||
>Note that if it doesn't work for you, then you should still be able to
|
||||
type the server name as \\SERVER in filemanager then hit enter and
|
||||
filemanager should display the list of available shares.</P
|
||||
><P
|
||||
>Some people find browsing fails because they don't have the global
|
||||
"guest account" set to a valid account. Remember that the IPC$
|
||||
connection that lists the shares is done as guest, and thus you must
|
||||
have a valid guest account.</P
|
||||
><P
|
||||
>Also, a lot of people are getting bitten by the problem of too many
|
||||
parameters on the command line of nmbd in inetd.conf. This trick is to
|
||||
not use spaces between the option and the parameter (eg: -d2 instead
|
||||
of -d 2), and to not use the -B and -N options. New versions of nmbd
|
||||
are now far more likely to correctly find your broadcast and network
|
||||
address, so in most cases these aren't needed.</P
|
||||
><P
|
||||
>The other big problem people have is that their broadcast address,
|
||||
netmask or IP address is wrong (specified with the "interfaces" option
|
||||
in smb.conf)</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN23"
|
||||
>Browsing across subnets</A
|
||||
></H1
|
||||
><P
|
||||
>With the release of Samba 1.9.17(alpha1 and above) Samba has been
|
||||
updated to enable it to support the replication of browse lists
|
||||
across subnet boundaries. New code and options have been added to
|
||||
achieve this. This section describes how to set this feature up
|
||||
in different settings.</P
|
||||
><P
|
||||
>To see browse lists that span TCP/IP subnets (ie. networks separated
|
||||
by routers that don't pass broadcast traffic) you must set up at least
|
||||
one WINS server. The WINS server acts as a DNS for NetBIOS names, allowing
|
||||
NetBIOS name to IP address translation to be done by doing a direct
|
||||
query of the WINS server. This is done via a directed UDP packet on
|
||||
port 137 to the WINS server machine. The reason for a WINS server is
|
||||
that by default, all NetBIOS name to IP address translation is done
|
||||
by broadcasts from the querying machine. This means that machines
|
||||
on one subnet will not be able to resolve the names of machines on
|
||||
another subnet without using a WINS server.</P
|
||||
><P
|
||||
>Remember, for browsing across subnets to work correctly, all machines,
|
||||
be they Windows 95, Windows NT, or Samba servers must have the IP address
|
||||
of a WINS server given to them by a DHCP server, or by manual configuration
|
||||
(for Win95 and WinNT, this is in the TCP/IP Properties, under Network
|
||||
settings) for Samba this is in the smb.conf file.</P
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN28"
|
||||
>How does cross subnet browsing work ?</A
|
||||
></H2
|
||||
><P
|
||||
>Cross subnet browsing is a complicated dance, containing multiple
|
||||
moving parts. It has taken Microsoft several years to get the code
|
||||
that achieves this correct, and Samba lags behind in some areas.
|
||||
However, with the 1.9.17 release, Samba is capable of cross subnet
|
||||
browsing when configured correctly.</P
|
||||
><P
|
||||
>Consider a network set up as follows :</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> (DMB)
|
||||
N1_A N1_B N1_C N1_D N1_E
|
||||
| | | | |
|
||||
-------------------------------------------------------
|
||||
| subnet 1 |
|
||||
+---+ +---+
|
||||
|R1 | Router 1 Router 2 |R2 |
|
||||
+---+ +---+
|
||||
| |
|
||||
| subnet 2 subnet 3 |
|
||||
-------------------------- ------------------------------------
|
||||
| | | | | | | |
|
||||
N2_A N2_B N2_C N2_D N3_A N3_B N3_C N3_D
|
||||
(WINS)</PRE
|
||||
></P
|
||||
><P
|
||||
>Consisting of 3 subnets (1, 2, 3) connected by two routers
|
||||
(R1, R2) - these do not pass broadcasts. Subnet 1 has 5 machines
|
||||
on it, subnet 2 has 4 machines, subnet 3 has 4 machines. Assume
|
||||
for the moment that all these machines are configured to be in the
|
||||
same workgroup (for simplicities sake). Machine N1_C on subnet 1
|
||||
is configured as Domain Master Browser (ie. it will collate the
|
||||
browse lists for the workgroup). Machine N2_D is configured as
|
||||
WINS server and all the other machines are configured to register
|
||||
their NetBIOS names with it.</P
|
||||
><P
|
||||
>As all these machines are booted up, elections for master browsers
|
||||
will take place on each of the three subnets. Assume that machine
|
||||
N1_C wins on subnet 1, N2_B wins on subnet 2, and N3_D wins on
|
||||
subnet 3 - these machines are known as local master browsers for
|
||||
their particular subnet. N1_C has an advantage in winning as the
|
||||
local master browser on subnet 1 as it is set up as Domain Master
|
||||
Browser.</P
|
||||
><P
|
||||
>On each of the three networks, machines that are configured to
|
||||
offer sharing services will broadcast that they are offering
|
||||
these services. The local master browser on each subnet will
|
||||
receive these broadcasts and keep a record of the fact that
|
||||
the machine is offering a service. This list of records is
|
||||
the basis of the browse list. For this case, assume that
|
||||
all the machines are configured to offer services so all machines
|
||||
will be on the browse list.</P
|
||||
><P
|
||||
>For each network, the local master browser on that network is
|
||||
considered 'authoritative' for all the names it receives via
|
||||
local broadcast. This is because a machine seen by the local
|
||||
master browser via a local broadcast must be on the same
|
||||
network as the local master browser and thus is a 'trusted'
|
||||
and 'verifiable' resource. Machines on other networks that
|
||||
the local master browsers learn about when collating their
|
||||
browse lists have not been directly seen - these records are
|
||||
called 'non-authoritative'.</P
|
||||
><P
|
||||
>At this point the browse lists look as follows (these are
|
||||
the machines you would see in your network neighborhood if
|
||||
you looked in it on a particular network right now).</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>Subnet Browse Master List
|
||||
------ ------------- ----
|
||||
Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E
|
||||
|
||||
Subnet2 N2_B N2_A, N2_B, N2_C, N2_D
|
||||
|
||||
Subnet3 N3_D N3_A, N3_B, N3_C, N3_D</PRE
|
||||
></P
|
||||
><P
|
||||
>Note that at this point all the subnets are separate, no
|
||||
machine is seen across any of the subnets.</P
|
||||
><P
|
||||
>Now examine subnet 2. As soon as N2_B has become the local
|
||||
master browser it looks for a Domain master browser to synchronize
|
||||
its browse list with. It does this by querying the WINS server
|
||||
(N2_D) for the IP address associated with the NetBIOS name
|
||||
WORKGROUP>1B<. This name was registerd by the Domain master
|
||||
browser (N1_C) with the WINS server as soon as it was booted.</P
|
||||
><P
|
||||
>Once N2_B knows the address of the Domain master browser it
|
||||
tells it that is the local master browser for subnet 2 by
|
||||
sending a MasterAnnouncement packet as a UDP port 138 packet.
|
||||
It then synchronizes with it by doing a NetServerEnum2 call. This
|
||||
tells the Domain Master Browser to send it all the server
|
||||
names it knows about. Once the domain master browser receives
|
||||
the MasterAnnouncement packet it schedules a synchronization
|
||||
request to the sender of that packet. After both synchronizations
|
||||
are done the browse lists look like :</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>Subnet Browse Master List
|
||||
------ ------------- ----
|
||||
Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E,
|
||||
N2_A(*), N2_B(*), N2_C(*), N2_D(*)
|
||||
|
||||
Subnet2 N2_B N2_A, N2_B, N2_C, N2_D
|
||||
N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*)
|
||||
|
||||
Subnet3 N3_D N3_A, N3_B, N3_C, N3_D
|
||||
|
||||
Servers with a (*) after them are non-authoritative names.</PRE
|
||||
></P
|
||||
><P
|
||||
>At this point users looking in their network neighborhood on
|
||||
subnets 1 or 2 will see all the servers on both, users on
|
||||
subnet 3 will still only see the servers on their own subnet.</P
|
||||
><P
|
||||
>The same sequence of events that occured for N2_B now occurs
|
||||
for the local master browser on subnet 3 (N3_D). When it
|
||||
synchronizes browse lists with the domain master browser (N1_A)
|
||||
it gets both the server entries on subnet 1, and those on
|
||||
subnet 2. After N3_D has synchronized with N1_C and vica-versa
|
||||
the browse lists look like.</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>Subnet Browse Master List
|
||||
------ ------------- ----
|
||||
Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E,
|
||||
N2_A(*), N2_B(*), N2_C(*), N2_D(*),
|
||||
N3_A(*), N3_B(*), N3_C(*), N3_D(*)
|
||||
|
||||
Subnet2 N2_B N2_A, N2_B, N2_C, N2_D
|
||||
N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*)
|
||||
|
||||
Subnet3 N3_D N3_A, N3_B, N3_C, N3_D
|
||||
N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*),
|
||||
N2_A(*), N2_B(*), N2_C(*), N2_D(*)
|
||||
|
||||
Servers with a (*) after them are non-authoritative names.</PRE
|
||||
></P
|
||||
><P
|
||||
>At this point users looking in their network neighborhood on
|
||||
subnets 1 or 3 will see all the servers on all sunbets, users on
|
||||
subnet 2 will still only see the servers on subnets 1 and 2, but not 3.</P
|
||||
><P
|
||||
>Finally, the local master browser for subnet 2 (N2_B) will sync again
|
||||
with the domain master browser (N1_C) and will recieve the missing
|
||||
server entries. Finally - and as a steady state (if no machines
|
||||
are removed or shut off) the browse lists will look like :</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>Subnet Browse Master List
|
||||
------ ------------- ----
|
||||
Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E,
|
||||
N2_A(*), N2_B(*), N2_C(*), N2_D(*),
|
||||
N3_A(*), N3_B(*), N3_C(*), N3_D(*)
|
||||
|
||||
Subnet2 N2_B N2_A, N2_B, N2_C, N2_D
|
||||
N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*)
|
||||
N3_A(*), N3_B(*), N3_C(*), N3_D(*)
|
||||
|
||||
Subnet3 N3_D N3_A, N3_B, N3_C, N3_D
|
||||
N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*),
|
||||
N2_A(*), N2_B(*), N2_C(*), N2_D(*)
|
||||
|
||||
Servers with a (*) after them are non-authoritative names.</PRE
|
||||
></P
|
||||
><P
|
||||
>Synchronizations between the domain master browser and local
|
||||
master browsers will continue to occur, but this should be a
|
||||
steady state situation.</P
|
||||
><P
|
||||
>If either router R1 or R2 fails the following will occur:</P
|
||||
><P
|
||||
></P
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
> Names of computers on each side of the inaccessible network fragments
|
||||
will be maintained for as long as 36 minutes, in the network neighbourhood
|
||||
lists.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Attempts to connect to these inaccessible computers will fail, but the
|
||||
names will not be removed from the network neighbourhood lists.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> If one of the fragments is cut off from the WINS server, it will only
|
||||
be able to access servers on its local subnet, by using subnet-isolated
|
||||
broadcast NetBIOS name resolution. The effects are similar to that of
|
||||
losing access to a DNS server.
|
||||
</P
|
||||
></LI
|
||||
></OL
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN63"
|
||||
>Setting up a WINS server</A
|
||||
></H1
|
||||
><P
|
||||
>Either a Samba machine or a Windows NT Server machine may be set up
|
||||
as a WINS server. To set a Samba machine to be a WINS server you must
|
||||
add the following option to the smb.conf file on the selected machine :
|
||||
in the [globals] section add the line </P
|
||||
><P
|
||||
><B
|
||||
CLASS="COMMAND"
|
||||
> wins support = yes</B
|
||||
></P
|
||||
><P
|
||||
>Versions of Samba previous to 1.9.17 had this parameter default to
|
||||
yes. If you have any older versions of Samba on your network it is
|
||||
strongly suggested you upgrade to 1.9.17 or above, or at the very
|
||||
least set the parameter to 'no' on all these machines.</P
|
||||
><P
|
||||
>Machines with "<B
|
||||
CLASS="COMMAND"
|
||||
>wins support = yes</B
|
||||
>" will keep a list of
|
||||
all NetBIOS names registered with them, acting as a DNS for NetBIOS names.</P
|
||||
><P
|
||||
>You should set up only ONE wins server. Do NOT set the
|
||||
"<B
|
||||
CLASS="COMMAND"
|
||||
>wins support = yes</B
|
||||
>" option on more than one Samba
|
||||
server.</P
|
||||
><P
|
||||
>To set up a Windows NT Server as a WINS server you need to set up
|
||||
the WINS service - see your NT documentation for details. Note that
|
||||
Windows NT WINS Servers can replicate to each other, allowing more
|
||||
than one to be set up in a complex subnet environment. As Microsoft
|
||||
refuse to document these replication protocols Samba cannot currently
|
||||
participate in these replications. It is possible in the future that
|
||||
a Samba->Samba WINS replication protocol may be defined, in which
|
||||
case more than one Samba machine could be set up as a WINS server
|
||||
but currently only one Samba server should have the "wins support = yes"
|
||||
parameter set.</P
|
||||
><P
|
||||
>After the WINS server has been configured you must ensure that all
|
||||
machines participating on the network are configured with the address
|
||||
of this WINS server. If your WINS server is a Samba machine, fill in
|
||||
the Samba machine IP address in the "Primary WINS Server" field of
|
||||
the "Control Panel->Network->Protocols->TCP->WINS Server" dialogs
|
||||
in Windows 95 or Windows NT. To tell a Samba server the IP address
|
||||
of the WINS server add the following line to the [global] section of
|
||||
all smb.conf files :</P
|
||||
><P
|
||||
><B
|
||||
CLASS="COMMAND"
|
||||
> wins server = >name or IP address<</B
|
||||
></P
|
||||
><P
|
||||
>where >name or IP address< is either the DNS name of the WINS server
|
||||
machine or its IP address.</P
|
||||
><P
|
||||
>Note that this line MUST NOT BE SET in the smb.conf file of the Samba
|
||||
server acting as the WINS server itself. If you set both the
|
||||
"<B
|
||||
CLASS="COMMAND"
|
||||
>wins support = yes</B
|
||||
>" option and the
|
||||
"<B
|
||||
CLASS="COMMAND"
|
||||
>wins server = >name<</B
|
||||
>" option then
|
||||
nmbd will fail to start.</P
|
||||
><P
|
||||
>There are two possible scenarios for setting up cross subnet browsing.
|
||||
The first details setting up cross subnet browsing on a network containing
|
||||
Windows 95, Samba and Windows NT machines that are not configured as
|
||||
part of a Windows NT Domain. The second details setting up cross subnet
|
||||
browsing on networks that contain NT Domains.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN82"
|
||||
>Setting up Browsing in a WORKGROUP</A
|
||||
></H1
|
||||
><P
|
||||
>To set up cross subnet browsing on a network containing machines
|
||||
in up to be in a WORKGROUP, not an NT Domain you need to set up one
|
||||
Samba server to be the Domain Master Browser (note that this is *NOT*
|
||||
the same as a Primary Domain Controller, although in an NT Domain the
|
||||
same machine plays both roles). The role of a Domain master browser is
|
||||
to collate the browse lists from local master browsers on all the
|
||||
subnets that have a machine participating in the workgroup. Without
|
||||
one machine configured as a domain master browser each subnet would
|
||||
be an isolated workgroup, unable to see any machines on any other
|
||||
subnet. It is the presense of a domain master browser that makes
|
||||
cross subnet browsing possible for a workgroup.</P
|
||||
><P
|
||||
>In an WORKGROUP environment the domain master browser must be a
|
||||
Samba server, and there must only be one domain master browser per
|
||||
workgroup name. To set up a Samba server as a domain master browser,
|
||||
set the following option in the [global] section of the smb.conf file :</P
|
||||
><P
|
||||
><B
|
||||
CLASS="COMMAND"
|
||||
> domain master = yes</B
|
||||
></P
|
||||
><P
|
||||
>The domain master browser should also preferrably be the local master
|
||||
browser for its own subnet. In order to achieve this set the following
|
||||
options in the [global] section of the smb.conf file :</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> domain master = yes
|
||||
local master = yes
|
||||
preferred master = yes
|
||||
os level = 65</PRE
|
||||
></P
|
||||
><P
|
||||
>The domain master browser may be the same machine as the WINS
|
||||
server, if you require.</P
|
||||
><P
|
||||
>Next, you should ensure that each of the subnets contains a
|
||||
machine that can act as a local master browser for the
|
||||
workgroup. Any NT machine should be able to do this, as will
|
||||
Windows 95 machines (although these tend to get rebooted more
|
||||
often, so it's not such a good idea to use these). To make a
|
||||
Samba server a local master browser set the following
|
||||
options in the [global] section of the smb.conf file :</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> domain master = no
|
||||
local master = yes
|
||||
preferred master = yes
|
||||
os level = 65</PRE
|
||||
></P
|
||||
><P
|
||||
>Do not do this for more than one Samba server on each subnet,
|
||||
or they will war with each other over which is to be the local
|
||||
master browser.</P
|
||||
><P
|
||||
>The "local master" parameter allows Samba to act as a local master
|
||||
browser. The "preferred master" causes nmbd to force a browser
|
||||
election on startup and the "os level" parameter sets Samba high
|
||||
enough so that it should win any browser elections.</P
|
||||
><P
|
||||
>If you have an NT machine on the subnet that you wish to
|
||||
be the local master browser then you can disable Samba from
|
||||
becoming a local master browser by setting the following
|
||||
options in the [global] section of the smb.conf file :</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> domain master = no
|
||||
local master = no
|
||||
preferred master = no
|
||||
os level = 0</PRE
|
||||
></P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN100"
|
||||
>Setting up Browsing in a DOMAIN</A
|
||||
></H1
|
||||
><P
|
||||
>If you are adding Samba servers to a Windows NT Domain then
|
||||
you must not set up a Samba server as a domain master browser.
|
||||
By default, a Windows NT Primary Domain Controller for a Domain
|
||||
name is also the Domain master browser for that name, and many
|
||||
things will break if a Samba server registers the Domain master
|
||||
browser NetBIOS name (DOMAIN>1B<) with WINS instead of the PDC.</P
|
||||
><P
|
||||
>For subnets other than the one containing the Windows NT PDC
|
||||
you may set up Samba servers as local master browsers as
|
||||
described. To make a Samba server a local master browser set
|
||||
the following options in the [global] section of the smb.conf
|
||||
file :</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> domain master = no
|
||||
local master = yes
|
||||
preferred master = yes
|
||||
os level = 65</PRE
|
||||
></P
|
||||
><P
|
||||
>If you wish to have a Samba server fight the election with machines
|
||||
on the same subnet you may set the "os level" parameter to lower
|
||||
levels. By doing this you can tune the order of machines that
|
||||
will become local master browsers if they are running. For
|
||||
more details on this see the section "FORCING SAMBA TO BE THE MASTER"
|
||||
below.</P
|
||||
><P
|
||||
>If you have Windows NT machines that are members of the domain
|
||||
on all subnets, and you are sure they will always be running then
|
||||
you can disable Samba from taking part in browser elections and
|
||||
ever becoming a local master browser by setting following options
|
||||
in the [global] section of the smb.conf file :</P
|
||||
><P
|
||||
><B
|
||||
CLASS="COMMAND"
|
||||
> domain master = no
|
||||
local master = no
|
||||
preferred master = no
|
||||
os level = 0</B
|
||||
></P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN110"
|
||||
>Forcing samba to be the master</A
|
||||
></H1
|
||||
><P
|
||||
>Who becomes the "master browser" is determined by an election process
|
||||
using broadcasts. Each election packet contains a number of parameters
|
||||
which determine what precedence (bias) a host should have in the
|
||||
election. By default Samba uses a very low precedence and thus loses
|
||||
elections to just about anyone else.</P
|
||||
><P
|
||||
>If you want Samba to win elections then just set the "os level" global
|
||||
option in smb.conf to a higher number. It defaults to 0. Using 34
|
||||
would make it win all elections over every other system (except other
|
||||
samba systems!)</P
|
||||
><P
|
||||
>A "os level" of 2 would make it beat WfWg and Win95, but not NTAS. A
|
||||
NTAS domain controller uses level 32.</P
|
||||
><P
|
||||
>The maximum os level is 255</P
|
||||
><P
|
||||
>If you want samba to force an election on startup, then set the
|
||||
"preferred master" global option in smb.conf to "yes". Samba will
|
||||
then have a slight advantage over other potential master browsers
|
||||
that are not preferred master browsers. Use this parameter with
|
||||
care, as if you have two hosts (whether they are windows 95 or NT or
|
||||
samba) on the same local subnet both set with "preferred master" to
|
||||
"yes", then periodically and continually they will force an election
|
||||
in order to become the local master browser.</P
|
||||
><P
|
||||
>If you want samba to be a "domain master browser", then it is
|
||||
recommended that you also set "preferred master" to "yes", because
|
||||
samba will not become a domain master browser for the whole of your
|
||||
LAN or WAN if it is not also a local master browser on its own
|
||||
broadcast isolated subnet.</P
|
||||
><P
|
||||
>It is possible to configure two samba servers to attempt to become
|
||||
the domain master browser for a domain. The first server that comes
|
||||
up will be the domain master browser. All other samba servers will
|
||||
attempt to become the domain master browser every 5 minutes. They
|
||||
will find that another samba server is already the domain master
|
||||
browser and will fail. This provides automatic redundancy, should
|
||||
the current domain master browser fail.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN119"
|
||||
>Making samba the domain master</A
|
||||
></H1
|
||||
><P
|
||||
>The domain master is responsible for collating the browse lists of
|
||||
multiple subnets so that browsing can occur between subnets. You can
|
||||
make samba act as the domain master by setting "domain master = yes"
|
||||
in smb.conf. By default it will not be a domain master.</P
|
||||
><P
|
||||
>Note that you should NOT set Samba to be the domain master for a
|
||||
workgroup that has the same name as an NT Domain.</P
|
||||
><P
|
||||
>When samba is the domain master and the master browser it will listen
|
||||
for master announcements (made roughly every twelve minutes) from local
|
||||
master browsers on other subnets and then contact them to synchronise
|
||||
browse lists.</P
|
||||
><P
|
||||
>If you want samba to be the domain master then I suggest you also set
|
||||
the "os level" high enough to make sure it wins elections, and set
|
||||
"preferred master" to "yes", to get samba to force an election on
|
||||
startup.</P
|
||||
><P
|
||||
>Note that all your servers (including samba) and clients should be
|
||||
using a WINS server to resolve NetBIOS names. If your clients are only
|
||||
using broadcasting to resolve NetBIOS names, then two things will occur:</P
|
||||
><P
|
||||
></P
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
> your local master browsers will be unable to find a domain master
|
||||
browser, as it will only be looking on the local subnet.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> if a client happens to get hold of a domain-wide browse list, and
|
||||
a user attempts to access a host in that list, it will be unable to
|
||||
resolve the NetBIOS name of that host.
|
||||
</P
|
||||
></LI
|
||||
></OL
|
||||
><P
|
||||
>If, however, both samba and your clients are using a WINS server, then:</P
|
||||
><P
|
||||
></P
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
> your local master browsers will contact the WINS server and, as long as
|
||||
samba has registered that it is a domain master browser with the WINS
|
||||
server, your local master browser will receive samba's ip address
|
||||
as its domain master browser.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> when a client receives a domain-wide browse list, and a user attempts
|
||||
to access a host in that list, it will contact the WINS server to
|
||||
resolve the NetBIOS name of that host. as long as that host has
|
||||
registered its NetBIOS name with the same WINS server, the user will
|
||||
be able to see that host.
|
||||
</P
|
||||
></LI
|
||||
></OL
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN137"
|
||||
>Note about broadcast addresses</A
|
||||
></H1
|
||||
><P
|
||||
>If your network uses a "0" based broadcast address (for example if it
|
||||
ends in a 0) then you will strike problems. Windows for Workgroups
|
||||
does not seem to support a 0's broadcast and you will probably find
|
||||
that browsing and name lookups won't work.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN140"
|
||||
>Multiple interfaces</A
|
||||
></H1
|
||||
><P
|
||||
>Samba now supports machines with multiple network interfaces. If you
|
||||
have multiple interfaces then you will need to use the "interfaces"
|
||||
option in smb.conf to configure them. See smb.conf(5) for details.</P
|
||||
></DIV
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
238
docs/htmldocs/Bugs.html
Normal file
238
docs/htmldocs/Bugs.html
Normal file
@ -0,0 +1,238 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Reporting Bugs</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD
|
||||
><BODY
|
||||
CLASS="ARTICLE"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="ARTICLE"
|
||||
><DIV
|
||||
CLASS="TITLEPAGE"
|
||||
><H1
|
||||
CLASS="TITLE"
|
||||
><A
|
||||
NAME="BUGREPORT"
|
||||
>Reporting Bugs</A
|
||||
></H1
|
||||
><HR></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN3"
|
||||
>Introduction</A
|
||||
></H1
|
||||
><P
|
||||
>The email address for bug reports is samba@samba.org</P
|
||||
><P
|
||||
>Please take the time to read this file before you submit a bug
|
||||
report. Also, please see if it has changed between releases, as we
|
||||
may be changing the bug reporting mechanism at some time.</P
|
||||
><P
|
||||
>Please also do as much as you can yourself to help track down the
|
||||
bug. Samba is maintained by a dedicated group of people who volunteer
|
||||
their time, skills and efforts. We receive far more mail about it than
|
||||
we can possibly answer, so you have a much higher chance of an answer
|
||||
and a fix if you send us a "developer friendly" bug report that lets
|
||||
us fix it fast. </P
|
||||
><P
|
||||
>Do not assume that if you post the bug to the comp.protocols.smb
|
||||
newsgroup or the mailing list that we will read it. If you suspect that your
|
||||
problem is not a bug but a configuration problem then it is better to send
|
||||
it to the Samba mailing list, as there are (at last count) 5000 other users on
|
||||
that list that may be able to help you.</P
|
||||
><P
|
||||
>You may also like to look though the recent mailing list archives,
|
||||
which are conveniently accessible on the Samba web pages
|
||||
at http://samba.org/samba/ </P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN10"
|
||||
>General info</A
|
||||
></H1
|
||||
><P
|
||||
>Before submitting a bug report check your config for silly
|
||||
errors. Look in your log files for obvious messages that tell you that
|
||||
you've misconfigured something and run testparm to test your config
|
||||
file for correct syntax.</P
|
||||
><P
|
||||
>Have you run through the <A
|
||||
HREF="Diagnosis.html"
|
||||
TARGET="_top"
|
||||
>diagnosis</A
|
||||
>?
|
||||
This is very important.</P
|
||||
><P
|
||||
>If you include part of a log file with your bug report then be sure to
|
||||
annotate it with exactly what you were doing on the client at the
|
||||
time, and exactly what the results were.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN16"
|
||||
>Debug levels</A
|
||||
></H1
|
||||
><P
|
||||
>If the bug has anything to do with Samba behaving incorrectly as a
|
||||
server (like refusing to open a file) then the log files will probably
|
||||
be very useful. Depending on the problem a log level of between 3 and
|
||||
10 showing the problem may be appropriate. A higher level givesmore
|
||||
detail, but may use too much disk space.</P
|
||||
><P
|
||||
>To set the debug level use <B
|
||||
CLASS="COMMAND"
|
||||
>log level =</B
|
||||
> in your
|
||||
<TT
|
||||
CLASS="FILENAME"
|
||||
>smb.conf</TT
|
||||
>. You may also find it useful to set the log
|
||||
level higher for just one machine and keep separate logs for each machine.
|
||||
To do this use:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>log level = 10
|
||||
log file = /usr/local/samba/lib/log.%m
|
||||
include = /usr/local/samba/lib/smb.conf.%m</PRE
|
||||
></P
|
||||
><P
|
||||
>then create a file
|
||||
<TT
|
||||
CLASS="FILENAME"
|
||||
>/usr/local/samba/lib/smb.conf.machine</TT
|
||||
> where
|
||||
"machine" is the name of the client you wish to debug. In that file
|
||||
put any smb.conf commands you want, for example
|
||||
<B
|
||||
CLASS="COMMAND"
|
||||
>log level=</B
|
||||
> may be useful. This also allows you to
|
||||
experiment with different security systems, protocol levels etc on just
|
||||
one machine.</P
|
||||
><P
|
||||
>The <TT
|
||||
CLASS="FILENAME"
|
||||
>smb.conf</TT
|
||||
> entry <B
|
||||
CLASS="COMMAND"
|
||||
>log level =</B
|
||||
>
|
||||
is synonymous with the entry <B
|
||||
CLASS="COMMAND"
|
||||
>debuglevel =</B
|
||||
> that has been
|
||||
used in older versions of Samba and is being retained for backwards
|
||||
compatibility of smb.conf files.</P
|
||||
><P
|
||||
>As the <B
|
||||
CLASS="COMMAND"
|
||||
>log level =</B
|
||||
> value is increased you will record
|
||||
a significantly increasing level of debugging information. For most
|
||||
debugging operations you may not need a setting higher than 3. Nearly
|
||||
all bugs can be tracked at a setting of 10, but be prepared for a VERY
|
||||
large volume of log data.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN33"
|
||||
>Internal errors</A
|
||||
></H1
|
||||
><P
|
||||
>If you get a "INTERNAL ERROR" message in your log files it means that
|
||||
Samba got an unexpected signal while running. It is probably a
|
||||
segmentation fault and almost certainly means a bug in Samba (unless
|
||||
you have faulty hardware or system software)</P
|
||||
><P
|
||||
>If the message came from smbd then it will probably be accompanied by
|
||||
a message which details the last SMB message received by smbd. This
|
||||
info is often very useful in tracking down the problem so please
|
||||
include it in your bug report.</P
|
||||
><P
|
||||
>You should also detail how to reproduce the problem, if
|
||||
possible. Please make this reasonably detailed.</P
|
||||
><P
|
||||
>You may also find that a core file appeared in a "corefiles"
|
||||
subdirectory of the directory where you keep your samba log
|
||||
files. This file is the most useful tool for tracking down the bug. To
|
||||
use it you do this:</P
|
||||
><P
|
||||
><B
|
||||
CLASS="COMMAND"
|
||||
>gdb smbd core</B
|
||||
></P
|
||||
><P
|
||||
>adding appropriate paths to smbd and core so gdb can find them. If you
|
||||
don't have gdb then try "dbx". Then within the debugger use the
|
||||
command "where" to give a stack trace of where the problem
|
||||
occurred. Include this in your mail.</P
|
||||
><P
|
||||
>If you known any assembly language then do a "disass" of the routine
|
||||
where the problem occurred (if its in a library routine then
|
||||
disassemble the routine that called it) and try to work out exactly
|
||||
where the problem is by looking at the surrounding code. Even if you
|
||||
don't know assembly then incuding this info in the bug report can be
|
||||
useful. </P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN43"
|
||||
>Attaching to a running process</A
|
||||
></H1
|
||||
><P
|
||||
>Unfortunately some unixes (in particular some recent linux kernels)
|
||||
refuse to dump a core file if the task has changed uid (which smbd
|
||||
does often). To debug with this sort of system you could try to attach
|
||||
to the running process using "gdb smbd PID" where you get PID from
|
||||
smbstatus. Then use "c" to continue and try to cause the core dump
|
||||
using the client. The debugger should catch the fault and tell you
|
||||
where it occurred.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN46"
|
||||
>Patches</A
|
||||
></H1
|
||||
><P
|
||||
>The best sort of bug report is one that includes a fix! If you send us
|
||||
patches please use <B
|
||||
CLASS="COMMAND"
|
||||
>diff -u</B
|
||||
> format if your version of
|
||||
diff supports it, otherwise use <B
|
||||
CLASS="COMMAND"
|
||||
>diff -c4</B
|
||||
>. Make sure
|
||||
your do the diff against a clean version of the source and let me know
|
||||
exactly what version you used. </P
|
||||
></DIV
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
548
docs/htmldocs/Diagnosis.html
Normal file
548
docs/htmldocs/Diagnosis.html
Normal file
@ -0,0 +1,548 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Diagnosing your samba server</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD
|
||||
><BODY
|
||||
CLASS="ARTICLE"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="ARTICLE"
|
||||
><DIV
|
||||
CLASS="TITLEPAGE"
|
||||
><H1
|
||||
CLASS="TITLE"
|
||||
><A
|
||||
NAME="DIAGNOSIS"
|
||||
>Diagnosing your samba server</A
|
||||
></H1
|
||||
><HR></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN3"
|
||||
>Introduction</A
|
||||
></H1
|
||||
><P
|
||||
>This file contains a list of tests you can perform to validate your
|
||||
Samba server. It also tells you what the likely cause of the problem
|
||||
is if it fails any one of these steps. If it passes all these tests
|
||||
then it is probably working fine.</P
|
||||
><P
|
||||
>You should do ALL the tests, in the order shown. I have tried to
|
||||
carefully choose them so later tests only use capabilities verified in
|
||||
the earlier tests.</P
|
||||
><P
|
||||
>If you send me an email saying "it doesn't work" and you have not
|
||||
followed this test procedure then you should not be surprised if I
|
||||
ignore your email.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN8"
|
||||
>Assumptions</A
|
||||
></H1
|
||||
><P
|
||||
>In all of the tests I assume you have a Samba server called BIGSERVER
|
||||
and a PC called ACLIENT both in workgroup TESTGROUP. I also assume the
|
||||
PC is running windows for workgroups with a recent copy of the
|
||||
microsoft tcp/ip stack. Alternatively, your PC may be running Windows
|
||||
95 or Windows NT (Workstation or Server).</P
|
||||
><P
|
||||
>The procedure is similar for other types of clients.</P
|
||||
><P
|
||||
>I also assume you know the name of an available share in your
|
||||
smb.conf. I will assume this share is called "tmp". You can add a
|
||||
"tmp" share like by adding the following to smb.conf:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> [tmp]
|
||||
comment = temporary files
|
||||
path = /tmp
|
||||
read only = yes </PRE
|
||||
></P
|
||||
><P
|
||||
>THESE TESTS ASSUME VERSION 2.0.6 OR LATER OF THE SAMBA SUITE. SOME
|
||||
COMMANDS SHOWN DID NOT EXIST IN EARLIER VERSIONS</P
|
||||
><P
|
||||
>Please pay attention to the error messages you receive. If any error message
|
||||
reports that your server is being unfriendly you should first check that you
|
||||
IP name resolution is correctly set up. eg: Make sure your /etc/resolv.conf
|
||||
file points to name servers that really do exist.</P
|
||||
><P
|
||||
>Also, if you do not have DNS server access for name resolution please check
|
||||
that the settings for your smb.conf file results in "dns proxy = no". The
|
||||
best way to check this is with "testparm smb.conf"</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN18"
|
||||
>Tests</A
|
||||
></H1
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN20"
|
||||
>Test 1</A
|
||||
></H2
|
||||
><P
|
||||
>In the directory in which you store your smb.conf file, run the command
|
||||
"testparm smb.conf". If it reports any errors then your smb.conf
|
||||
configuration file is faulty.</P
|
||||
><P
|
||||
>Note: Your smb.conf file may be located in: <TT
|
||||
CLASS="FILENAME"
|
||||
>/etc</TT
|
||||
>
|
||||
Or in: <TT
|
||||
CLASS="FILENAME"
|
||||
>/usr/local/samba/lib</TT
|
||||
></P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN26"
|
||||
>Test 2</A
|
||||
></H2
|
||||
><P
|
||||
>Run the command "ping BIGSERVER" from the PC and "ping ACLIENT" from
|
||||
the unix box. If you don't get a valid response then your TCP/IP
|
||||
software is not correctly installed. </P
|
||||
><P
|
||||
>Note that you will need to start a "dos prompt" window on the PC to
|
||||
run ping.</P
|
||||
><P
|
||||
>If you get a message saying "host not found" or similar then your DNS
|
||||
software or /etc/hosts file is not correctly setup. It is possible to
|
||||
run samba without DNS entries for the server and client, but I assume
|
||||
you do have correct entries for the remainder of these tests. </P
|
||||
><P
|
||||
>Another reason why ping might fail is if your host is running firewall
|
||||
software. You will need to relax the rules to let in the workstation
|
||||
in question, perhaps by allowing access from another subnet (on Linux
|
||||
this is done via the ipfwadm program.)</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN32"
|
||||
>Test 3</A
|
||||
></H2
|
||||
><P
|
||||
>Run the command "smbclient -L BIGSERVER" on the unix box. You
|
||||
should get a list of available shares back. </P
|
||||
><P
|
||||
>If you get a error message containing the string "Bad password" then
|
||||
you probably have either an incorrect "hosts allow", "hosts deny" or
|
||||
"valid users" line in your smb.conf, or your guest account is not
|
||||
valid. Check what your guest account is using "testparm" and
|
||||
temporarily remove any "hosts allow", "hosts deny", "valid users" or
|
||||
"invalid users" lines.</P
|
||||
><P
|
||||
>If you get a "connection refused" response then the smbd server may
|
||||
not be running. If you installed it in inetd.conf then you probably edited
|
||||
that file incorrectly. If you installed it as a daemon then check that
|
||||
it is running, and check that the netbios-ssn port is in a LISTEN
|
||||
state using "netstat -a".</P
|
||||
><P
|
||||
>If you get a "session request failed" then the server refused the
|
||||
connection. If it says "Your server software is being unfriendly" then
|
||||
its probably because you have invalid command line parameters to smbd,
|
||||
or a similar fatal problem with the initial startup of smbd. Also
|
||||
check your config file (smb.conf) for syntax errors with "testparm"
|
||||
and that the various directories where samba keeps its log and lock
|
||||
files exist.</P
|
||||
><P
|
||||
>There are a number of reasons for which smbd may refuse or decline
|
||||
a session request. The most common of these involve one or more of
|
||||
the following smb.conf file entries:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> hosts deny = ALL
|
||||
hosts allow = xxx.xxx.xxx.xxx/yy
|
||||
bind interfaces only = Yes</PRE
|
||||
></P
|
||||
><P
|
||||
>In the above, no allowance has been made for any session requests that
|
||||
will automatically translate to the loopback adaptor address 127.0.0.1.
|
||||
To solve this problem change these lines to:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> hosts deny = ALL
|
||||
hosts allow = xxx.xxx.xxx.xxx/yy 127.</PRE
|
||||
></P
|
||||
><P
|
||||
>Do NOT use the "bind interfaces only" parameter where you may wish to
|
||||
use the samba password change facility, or where smbclient may need to
|
||||
access local service for name resolution or for local resource
|
||||
connections. (Note: the "bind interfaces only" parameter deficiency
|
||||
where it will not allow connections to the loopback address will be
|
||||
fixed soon).</P
|
||||
><P
|
||||
>Another common cause of these two errors is having something already running
|
||||
on port 139, such as Samba (ie: smbd is running from inetd already) or
|
||||
something like Digital's Pathworks. Check your inetd.conf file before trying
|
||||
to start smbd as a daemon, it can avoid a lot of frustration!</P
|
||||
><P
|
||||
>And yet another possible cause for failure of TEST 3 is when the subnet mask
|
||||
and / or broadcast address settings are incorrect. Please check that the
|
||||
network interface IP Address / Broadcast Address / Subnet Mask settings are
|
||||
correct and that Samba has correctly noted these in the log.nmb file.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN47"
|
||||
>Test 4</A
|
||||
></H2
|
||||
><P
|
||||
>Run the command "nmblookup -B BIGSERVER __SAMBA__". You should get the
|
||||
IP address of your Samba server back.</P
|
||||
><P
|
||||
>If you don't then nmbd is incorrectly installed. Check your inetd.conf
|
||||
if you run it from there, or that the daemon is running and listening
|
||||
to udp port 137.</P
|
||||
><P
|
||||
>One common problem is that many inetd implementations can't take many
|
||||
parameters on the command line. If this is the case then create a
|
||||
one-line script that contains the right parameters and run that from
|
||||
inetd.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN52"
|
||||
>Test 5</A
|
||||
></H2
|
||||
><P
|
||||
>run the command <B
|
||||
CLASS="COMMAND"
|
||||
>nmblookup -B ACLIENT '*'</B
|
||||
></P
|
||||
><P
|
||||
>You should get the PCs IP address back. If you don't then the client
|
||||
software on the PC isn't installed correctly, or isn't started, or you
|
||||
got the name of the PC wrong. </P
|
||||
><P
|
||||
>If ACLIENT doesn't resolve via DNS then use the IP address of the
|
||||
client in the above test.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN58"
|
||||
>Test 6</A
|
||||
></H2
|
||||
><P
|
||||
>Run the command <B
|
||||
CLASS="COMMAND"
|
||||
>nmblookup -d 2 '*'</B
|
||||
></P
|
||||
><P
|
||||
>This time we are trying the same as the previous test but are trying
|
||||
it via a broadcast to the default broadcast address. A number of
|
||||
Netbios/TCPIP hosts on the network should respond, although Samba may
|
||||
not catch all of the responses in the short time it listens. You
|
||||
should see "got a positive name query response" messages from several
|
||||
hosts.</P
|
||||
><P
|
||||
>If this doesn't give a similar result to the previous test then
|
||||
nmblookup isn't correctly getting your broadcast address through its
|
||||
automatic mechanism. In this case you should experiment use the
|
||||
"interfaces" option in smb.conf to manually configure your IP
|
||||
address, broadcast and netmask. </P
|
||||
><P
|
||||
>If your PC and server aren't on the same subnet then you will need to
|
||||
use the -B option to set the broadcast address to the that of the PCs
|
||||
subnet.</P
|
||||
><P
|
||||
>This test will probably fail if your subnet mask and broadcast address are
|
||||
not correct. (Refer to TEST 3 notes above).</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN66"
|
||||
>Test 7</A
|
||||
></H2
|
||||
><P
|
||||
>Run the command <B
|
||||
CLASS="COMMAND"
|
||||
>smbclient //BIGSERVER/TMP</B
|
||||
>. You should
|
||||
then be prompted for a password. You should use the password of the account
|
||||
you are logged into the unix box with. If you want to test with
|
||||
another account then add the -U >accountname< option to the end of
|
||||
the command line. eg:
|
||||
<B
|
||||
CLASS="COMMAND"
|
||||
>smbclient //bigserver/tmp -Ujohndoe</B
|
||||
></P
|
||||
><P
|
||||
>Note: It is possible to specify the password along with the username
|
||||
as follows:
|
||||
<B
|
||||
CLASS="COMMAND"
|
||||
>smbclient //bigserver/tmp -Ujohndoe%secret</B
|
||||
></P
|
||||
><P
|
||||
>Once you enter the password you should get the "smb>" prompt. If you
|
||||
don't then look at the error message. If it says "invalid network
|
||||
name" then the service "tmp" is not correctly setup in your smb.conf.</P
|
||||
><P
|
||||
>If it says "bad password" then the likely causes are:</P
|
||||
><P
|
||||
></P
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
> you have shadow passords (or some other password system) but didn't
|
||||
compile in support for them in smbd
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> your "valid users" configuration is incorrect
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> you have a mixed case password and you haven't enabled the "password
|
||||
level" option at a high enough level
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> the "path =" line in smb.conf is incorrect. Check it with testparm
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> you enabled password encryption but didn't create the SMB encrypted
|
||||
password file
|
||||
</P
|
||||
></LI
|
||||
></OL
|
||||
><P
|
||||
>Once connected you should be able to use the commands
|
||||
<B
|
||||
CLASS="COMMAND"
|
||||
>dir</B
|
||||
> <B
|
||||
CLASS="COMMAND"
|
||||
>get</B
|
||||
> <B
|
||||
CLASS="COMMAND"
|
||||
>put</B
|
||||
> etc.
|
||||
Type <B
|
||||
CLASS="COMMAND"
|
||||
>help >command<</B
|
||||
> for instructions. You should
|
||||
especially check that the amount of free disk space shown is correct
|
||||
when you type <B
|
||||
CLASS="COMMAND"
|
||||
>dir</B
|
||||
>.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN92"
|
||||
>Test 8</A
|
||||
></H2
|
||||
><P
|
||||
>On the PC type the command <B
|
||||
CLASS="COMMAND"
|
||||
>net view \\BIGSERVER</B
|
||||
>. You will
|
||||
need to do this from within a "dos prompt" window. You should get back a
|
||||
list of available shares on the server.</P
|
||||
><P
|
||||
>If you get a "network name not found" or similar error then netbios
|
||||
name resolution is not working. This is usually caused by a problem in
|
||||
nmbd. To overcome it you could do one of the following (you only need
|
||||
to choose one of them):</P
|
||||
><P
|
||||
></P
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
> fixup the nmbd installation</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> add the IP address of BIGSERVER to the "wins server" box in the
|
||||
advanced tcp/ip setup on the PC.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> enable windows name resolution via DNS in the advanced section of
|
||||
the tcp/ip setup</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> add BIGSERVER to your lmhosts file on the PC.</P
|
||||
></LI
|
||||
></OL
|
||||
><P
|
||||
>If you get a "invalid network name" or "bad password error" then the
|
||||
same fixes apply as they did for the "smbclient -L" test above. In
|
||||
particular, make sure your "hosts allow" line is correct (see the man
|
||||
pages)</P
|
||||
><P
|
||||
>Also, do not overlook that fact that when the workstation requests the
|
||||
connection to the samba server it will attempt to connect using the
|
||||
name with which you logged onto your Windows machine. You need to make
|
||||
sure that an account exists on your Samba server with that exact same
|
||||
name and password.</P
|
||||
><P
|
||||
>If you get "specified computer is not receiving requests" or similar
|
||||
it probably means that the host is not contactable via tcp services.
|
||||
Check to see if the host is running tcp wrappers, and if so add an entry in
|
||||
the hosts.allow file for your client (or subnet, etc.)</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN109"
|
||||
>Test 9</A
|
||||
></H2
|
||||
><P
|
||||
>Run the command <B
|
||||
CLASS="COMMAND"
|
||||
>net use x: \\BIGSERVER\TMP</B
|
||||
>. You should
|
||||
be prompted for a password then you should get a "command completed
|
||||
successfully" message. If not then your PC software is incorrectly
|
||||
installed or your smb.conf is incorrect. make sure your "hosts allow"
|
||||
and other config lines in smb.conf are correct.</P
|
||||
><P
|
||||
>It's also possible that the server can't work out what user name to
|
||||
connect you as. To see if this is the problem add the line "user =
|
||||
USERNAME" to the [tmp] section of smb.conf where "USERNAME" is the
|
||||
username corresponding to the password you typed. If you find this
|
||||
fixes things you may need the username mapping option.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN114"
|
||||
>Test 10</A
|
||||
></H2
|
||||
><P
|
||||
>Run the command <B
|
||||
CLASS="COMMAND"
|
||||
>nmblookup -M TESTGROUP</B
|
||||
> where
|
||||
TESTGROUP is the name of the workgroup that your Samba server and
|
||||
Windows PCs belong to. You should get back the IP address of the
|
||||
master browser for that workgroup.</P
|
||||
><P
|
||||
>If you don't then the election process has failed. Wait a minute to
|
||||
see if it is just being slow then try again. If it still fails after
|
||||
that then look at the browsing options you have set in smb.conf. Make
|
||||
sure you have <B
|
||||
CLASS="COMMAND"
|
||||
>preferred master = yes</B
|
||||
> to ensure that
|
||||
an election is held at startup.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN120"
|
||||
>Test 11</A
|
||||
></H2
|
||||
><P
|
||||
>From file manager try to browse the server. Your samba server should
|
||||
appear in the browse list of your local workgroup (or the one you
|
||||
specified in smb.conf). You should be able to double click on the name
|
||||
of the server and get a list of shares. If you get a "invalid
|
||||
password" error when you do then you are probably running WinNT and it
|
||||
is refusing to browse a server that has no encrypted password
|
||||
capability and is in user level security mode. In this case either set
|
||||
<B
|
||||
CLASS="COMMAND"
|
||||
>security = server</B
|
||||
> AND
|
||||
<B
|
||||
CLASS="COMMAND"
|
||||
>password server = Windows_NT_Machine</B
|
||||
> in your
|
||||
smb.conf file, or enable encrypted passwords AFTER compiling in support
|
||||
for encrypted passwords (refer to the Makefile).</P
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN125"
|
||||
>Still having troubles?</A
|
||||
></H1
|
||||
><P
|
||||
>Try the mailing list or newsgroup, or use the ethereal utility to
|
||||
sniff the problem. The official samba mailing list can be reached at
|
||||
<A
|
||||
HREF="mailto:samba@samba.org"
|
||||
TARGET="_top"
|
||||
>samba@samba.org</A
|
||||
>. To find
|
||||
out more about samba and how to subscribe to the mailing list check
|
||||
out the samba web page at
|
||||
<A
|
||||
HREF="http://samba.org/samba"
|
||||
TARGET="_top"
|
||||
>http://samba.org/samba</A
|
||||
></P
|
||||
><P
|
||||
>Also look at the other docs in the Samba package!</P
|
||||
></DIV
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
408
docs/htmldocs/Printing.html
Normal file
408
docs/htmldocs/Printing.html
Normal file
@ -0,0 +1,408 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Debugging Printing Problems</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD
|
||||
><BODY
|
||||
CLASS="ARTICLE"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="ARTICLE"
|
||||
><DIV
|
||||
CLASS="TITLEPAGE"
|
||||
><H1
|
||||
CLASS="TITLE"
|
||||
><A
|
||||
NAME="PRINTING_DEBUG"
|
||||
>Debugging Printing Problems</A
|
||||
></H1
|
||||
><HR></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN3"
|
||||
>Introduction</A
|
||||
></H1
|
||||
><P
|
||||
>This is a short description of how to debug printing problems with
|
||||
Samba. This describes how to debug problems with printing from a SMB
|
||||
client to a Samba server, not the other way around. For the reverse
|
||||
see the examples/printing directory.</P
|
||||
><P
|
||||
>Ok, so you want to print to a Samba server from your PC. The first
|
||||
thing you need to understand is that Samba does not actually do any
|
||||
printing itself, it just acts as a middleman between your PC client
|
||||
and your Unix printing subsystem. Samba receives the file from the PC
|
||||
then passes the file to a external "print command". What print command
|
||||
you use is up to you.</P
|
||||
><P
|
||||
>The whole things is controlled using options in smb.conf. The most
|
||||
relevant options (which you should look up in the smb.conf man page)
|
||||
are:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> [global]
|
||||
print command - send a file to a spooler
|
||||
lpq command - get spool queue status
|
||||
lprm command - remove a job
|
||||
[printers]
|
||||
path = /var/spool/lpd/samba</PRE
|
||||
></P
|
||||
><P
|
||||
>The following are nice to know about:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> queuepause command - stop a printer or print queue
|
||||
queueresume command - start a printer or print queue</PRE
|
||||
></P
|
||||
><P
|
||||
>Example:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> print command = /usr/bin/lpr -r -P%p %s
|
||||
lpq command = /usr/bin/lpq -P%p %s
|
||||
lprm command = /usr/bin/lprm -P%p %j
|
||||
queuepause command = /usr/sbin/lpc -P%p stop
|
||||
queuepause command = /usr/sbin/lpc -P%p start</PRE
|
||||
></P
|
||||
><P
|
||||
>Samba should set reasonable defaults for these depending on your
|
||||
system type, but it isn't clairvoyant. It is not uncommon that you
|
||||
have to tweak these for local conditions. The commands should
|
||||
always have fully specified pathnames, as the smdb may not have
|
||||
the correct PATH values.</P
|
||||
><P
|
||||
>When you send a job to Samba to be printed, it will make a temporary
|
||||
copy of it in the directory specified in the [printers] section.
|
||||
and it should be periodically cleaned out. The lpr -r option
|
||||
requests that the temporary copy be removed after printing; If
|
||||
printing fails then you might find leftover files in this directory,
|
||||
and it should be periodically cleaned out. Samba used the lpq
|
||||
command to determine the "job number" assigned to your print job
|
||||
by the spooler.</P
|
||||
><P
|
||||
>The %>letter< are "macros" that get dynamically replaced with appropriate
|
||||
values when they are used. The %s gets replaced with the name of the spool
|
||||
file that Samba creates and the %p gets replaced with the name of the
|
||||
printer. The %j gets replaced with the "job number" which comes from
|
||||
the lpq output.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN19"
|
||||
>Debugging printer problems</A
|
||||
></H1
|
||||
><P
|
||||
>One way to debug printing problems is to start by replacing these
|
||||
command with shell scripts that record the arguments and the contents
|
||||
of the print file. A simple example of this kind of things might
|
||||
be:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> print command = /tmp/saveprint %p %s
|
||||
|
||||
#!/bin/saveprint
|
||||
# we make sure that we are the right user
|
||||
/usr/bin/id -p >/tmp/tmp.print
|
||||
# we run the command and save the error messages
|
||||
# replace the command with the one appropriate for your system
|
||||
/usr/bin/lpr -r -P$1 $2 2>>&/tmp/tmp.print</PRE
|
||||
></P
|
||||
><P
|
||||
>Then you print a file and try removing it. You may find that the
|
||||
print queue needs to be stopped in order to see the queue status
|
||||
and remove the job:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> h4: {42} % echo hi >/tmp/hi
|
||||
h4: {43} % smbclient //localhost/lw4
|
||||
added interface ip=10.0.0.4 bcast=10.0.0.255 nmask=255.255.255.0
|
||||
Password:
|
||||
Domain=[ASTART] OS=[Unix] Server=[Samba 2.0.7]
|
||||
smb: \> print /tmp/hi
|
||||
putting file /tmp/hi as hi-17534 (0.0 kb/s) (average 0.0 kb/s)
|
||||
smb: \> queue
|
||||
1049 3 hi-17534
|
||||
smb: \> cancel 1049
|
||||
Error cancelling job 1049 : code 0
|
||||
smb: \> cancel 1049
|
||||
Job 1049 cancelled
|
||||
smb: \> queue
|
||||
smb: \> exit</PRE
|
||||
></P
|
||||
><P
|
||||
>The 'code 0' indicates that the job was removed. The comment
|
||||
by the smbclient is a bit misleading on this.
|
||||
You can observe the command output and then and look at the
|
||||
/tmp/tmp.print file to see what the results are. You can quickly
|
||||
find out if the problem is with your printing system. Often people
|
||||
have problems with their /etc/printcap file or permissions on
|
||||
various print queues.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN28"
|
||||
>What printers do I have?</A
|
||||
></H1
|
||||
><P
|
||||
>You can use the 'testprns' program to check to see if the printer
|
||||
name you are using is recognized by Samba. For example, you can
|
||||
use:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> testprns printer /etc/printcap</PRE
|
||||
></P
|
||||
><P
|
||||
>Samba can get its printcap information from a file or from a program.
|
||||
You can try the following to see the format of the extracted
|
||||
information:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> testprns -a printer /etc/printcap
|
||||
|
||||
testprns -a printer '|/bin/cat printcap'</PRE
|
||||
></P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN36"
|
||||
>Setting up printcap and print servers</A
|
||||
></H1
|
||||
><P
|
||||
>You may need to set up some printcaps for your Samba system to use.
|
||||
It is strongly recommended that you use the facilities provided by
|
||||
the print spooler to set up queues and printcap information.</P
|
||||
><P
|
||||
>Samba requires either a printcap or program to deliver printcap
|
||||
information. This printcap information has the format:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> name|alias1|alias2...:option=value:...</PRE
|
||||
></P
|
||||
><P
|
||||
>For almost all printing systems, the printer 'name' must be composed
|
||||
only of alphanumeric or underscore '_' characters. Some systems also
|
||||
allow hyphens ('-') as well. An alias is an alternative name for the
|
||||
printer, and an alias with a space in it is used as a 'comment'
|
||||
about the printer. The printcap format optionally uses a \ at the end of lines
|
||||
to extend the printcap to multiple lines.</P
|
||||
><P
|
||||
>Here are some examples of printcap files:</P
|
||||
><P
|
||||
><P
|
||||
></P
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
>pr just printer name</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>pr|alias printer name and alias</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>pr|My Printer printer name, alias used as comment</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>pr:sh:\ Same as pr:sh:cm= testing
|
||||
:cm= \
|
||||
testing</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>pr:sh Same as pr:sh:cm= testing
|
||||
:cm= testing</P
|
||||
></LI
|
||||
></OL
|
||||
></P
|
||||
><P
|
||||
>Samba reads the printcap information when first started. If you make
|
||||
changes in the printcap information, then you must do the following:</P
|
||||
><P
|
||||
></P
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
>make sure that the print spooler is aware of these changes.
|
||||
The LPRng system uses the 'lpc reread' command to do this.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>make sure that the spool queues, etc., exist and have the
|
||||
correct permissions. The LPRng system uses the 'checkpc -f'
|
||||
command to do this.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>You now should send a SIGHUP signal to the smbd server to have
|
||||
it reread the printcap information.</P
|
||||
></LI
|
||||
></OL
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN64"
|
||||
>Job sent, no output</A
|
||||
></H1
|
||||
><P
|
||||
>This is the most frustrating part of printing. You may have sent the
|
||||
job, verified that the job was forwarded, set up a wrapper around
|
||||
the command to send the file, but there was no output from the printer.</P
|
||||
><P
|
||||
>First, check to make sure that the job REALLY is getting to the
|
||||
right print queue. If you are using a BSD or LPRng print spooler,
|
||||
you can temporarily stop the printing of jobs. Jobs can still be
|
||||
submitted, but they will not be printed. Use:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> lpc -Pprinter stop</PRE
|
||||
></P
|
||||
><P
|
||||
>Now submit a print job and then use 'lpq -Pprinter' to see if the
|
||||
job is in the print queue. If it is not in the print queue then
|
||||
you will have to find out why it is not being accepted for printing.</P
|
||||
><P
|
||||
>Next, you may want to check to see what the format of the job really
|
||||
was. With the assistance of the system administrator you can view
|
||||
the submitted jobs files. You may be surprised to find that these
|
||||
are not in what you would expect to call a printable format.
|
||||
You can use the UNIX 'file' utitily to determine what the job
|
||||
format actually is:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> cd /var/spool/lpd/printer # spool directory of print jobs
|
||||
ls # find job files
|
||||
file dfA001myhost</PRE
|
||||
></P
|
||||
><P
|
||||
>You should make sure that your printer supports this format OR that
|
||||
your system administrator has installed a 'print filter' that will
|
||||
convert the file to a format appropriate for your printer.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN75"
|
||||
>Job sent, strange output</A
|
||||
></H1
|
||||
><P
|
||||
>Once you have the job printing, you can then start worrying about
|
||||
making it print nicely.</P
|
||||
><P
|
||||
>The most common problem is extra pages of output: banner pages
|
||||
OR blank pages at the end.</P
|
||||
><P
|
||||
>If you are getting banner pages, check and make sure that the
|
||||
printcap option or printer option is configured for no banners.
|
||||
If you have a printcap, this is the :sh (suppress header or banner
|
||||
page) option. You should have the following in your printer.</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> printer: ... :sh</PRE
|
||||
></P
|
||||
><P
|
||||
>If you have this option and are still getting banner pages, there
|
||||
is a strong chance that your printer is generating them for you
|
||||
automatically. You should make sure that banner printing is disabled
|
||||
for the printer. This usually requires using the printer setup software
|
||||
or procedures supplied by the printer manufacturer.</P
|
||||
><P
|
||||
>If you get an extra page of output, this could be due to problems
|
||||
with your job format, or if you are generating PostScript jobs,
|
||||
incorrect setting on your printer driver on the MicroSoft client.
|
||||
For example, under Win95 there is a option:</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> Printers|Printer Name|(Right Click)Properties|Postscript|Advanced|</PRE
|
||||
></P
|
||||
><P
|
||||
>that allows you to choose if a Ctrl-D is appended to all jobs.
|
||||
This is a very bad thing to do, as most spooling systems will
|
||||
automatically add a ^D to the end of the job if it is detected as
|
||||
PostScript. The multiple ^D may cause an additional page of output.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN87"
|
||||
>Raw PostScript printed</A
|
||||
></H1
|
||||
><P
|
||||
>This is a problem that is usually caused by either the print spooling
|
||||
system putting information at the start of the print job that makes
|
||||
the printer think the job is a text file, or your printer simply
|
||||
does not support PostScript. You may need to enable 'Automatic
|
||||
Format Detection' on your printer.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN90"
|
||||
>Advanced Printing</A
|
||||
></H1
|
||||
><P
|
||||
>Note that you can do some pretty magic things by using your
|
||||
imagination with the "print command" option and some shell scripts.
|
||||
Doing print accounting is easy by passing the %U option to a print
|
||||
command shell script. You could even make the print command detect
|
||||
the type of output and its size and send it to an appropriate
|
||||
printer.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN93"
|
||||
>Real debugging</A
|
||||
></H1
|
||||
><P
|
||||
>If the above debug tips don't help, then maybe you need to bring in
|
||||
the bug guns, system tracing. See Tracing.txt in this directory.</P
|
||||
></DIV
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
550
docs/htmldocs/Speed.html
Normal file
550
docs/htmldocs/Speed.html
Normal file
@ -0,0 +1,550 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Samba performance issues</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD
|
||||
><BODY
|
||||
CLASS="ARTICLE"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="ARTICLE"
|
||||
><DIV
|
||||
CLASS="TITLEPAGE"
|
||||
><H1
|
||||
CLASS="TITLE"
|
||||
><A
|
||||
NAME="SPEED"
|
||||
>Samba performance issues</A
|
||||
></H1
|
||||
><HR></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN3"
|
||||
>Comparisons</A
|
||||
></H1
|
||||
><P
|
||||
>The Samba server uses TCP to talk to the client. Thus if you are
|
||||
trying to see if it performs well you should really compare it to
|
||||
programs that use the same protocol. The most readily available
|
||||
programs for file transfer that use TCP are ftp or another TCP based
|
||||
SMB server.</P
|
||||
><P
|
||||
>If you want to test against something like a NT or WfWg server then
|
||||
you will have to disable all but TCP on either the client or
|
||||
server. Otherwise you may well be using a totally different protocol
|
||||
(such as Netbeui) and comparisons may not be valid.</P
|
||||
><P
|
||||
>Generally you should find that Samba performs similarly to ftp at raw
|
||||
transfer speed. It should perform quite a bit faster than NFS,
|
||||
although this very much depends on your system.</P
|
||||
><P
|
||||
>Several people have done comparisons between Samba and Novell, NFS or
|
||||
WinNT. In some cases Samba performed the best, in others the worst. I
|
||||
suspect the biggest factor is not Samba vs some other system but the
|
||||
hardware and drivers used on the various systems. Given similar
|
||||
hardware Samba should certainly be competitive in speed with other
|
||||
systems.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN9"
|
||||
>Oplocks</A
|
||||
></H1
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN11"
|
||||
>Overview</A
|
||||
></H2
|
||||
><P
|
||||
>Oplocks are the way that SMB clients get permission from a server to
|
||||
locally cache file operations. If a server grants an oplock
|
||||
(opportunistic lock) then the client is free to assume that it is the
|
||||
only one accessing the file and it will agressively cache file
|
||||
data. With some oplock types the client may even cache file open/close
|
||||
operations. This can give enormous performance benefits.</P
|
||||
><P
|
||||
>With the release of Samba 1.9.18 we now correctly support opportunistic
|
||||
locks. This is turned on by default, and can be turned off on a share-
|
||||
by-share basis by setting the parameter :</P
|
||||
><P
|
||||
><B
|
||||
CLASS="COMMAND"
|
||||
>oplocks = False</B
|
||||
></P
|
||||
><P
|
||||
>We recommend that you leave oplocks on however, as current benchmark
|
||||
tests with NetBench seem to give approximately a 30% improvement in
|
||||
speed with them on. This is on average however, and the actual
|
||||
improvement seen can be orders of magnitude greater, depending on
|
||||
what the client redirector is doing.</P
|
||||
><P
|
||||
>Previous to Samba 1.9.18 there was a 'fake oplocks' option. This
|
||||
option has been left in the code for backwards compatibility reasons
|
||||
but it's use is now deprecated. A short summary of what the old
|
||||
code did follows.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN19"
|
||||
>Level2 Oplocks</A
|
||||
></H2
|
||||
><P
|
||||
>With Samba 2.0.5 a new capability - level2 (read only) oplocks is
|
||||
supported (although the option is off by default - see the smb.conf
|
||||
man page for details). Turning on level2 oplocks (on a share-by-share basis)
|
||||
by setting the parameter :</P
|
||||
><P
|
||||
><B
|
||||
CLASS="COMMAND"
|
||||
>level2 oplocks = true</B
|
||||
></P
|
||||
><P
|
||||
>should speed concurrent access to files that are not commonly written
|
||||
to, such as application serving shares (ie. shares that contain common
|
||||
.EXE files - such as a Microsoft Office share) as it allows clients to
|
||||
read-ahread cache copies of these files.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT2"
|
||||
><HR><H2
|
||||
CLASS="SECT2"
|
||||
><A
|
||||
NAME="AEN25"
|
||||
>Old 'fake oplocks' option - deprecated</A
|
||||
></H2
|
||||
><P
|
||||
>Samba can also fake oplocks, by granting a oplock whenever a client
|
||||
asks for one. This is controlled using the smb.conf option "fake
|
||||
oplocks". If you set "fake oplocks = yes" then you are telling the
|
||||
client that it may agressively cache the file data for all opens.</P
|
||||
><P
|
||||
>Enabling 'fake oplocks' on all read-only shares or shares that you know
|
||||
will only be accessed from one client at a time you will see a big
|
||||
performance improvement on many operations. If you enable this option
|
||||
on shares where multiple clients may be accessing the files read-write
|
||||
at the same time you can get data corruption.</P
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN29"
|
||||
>Socket options</A
|
||||
></H1
|
||||
><P
|
||||
>There are a number of socket options that can greatly affect the
|
||||
performance of a TCP based server like Samba.</P
|
||||
><P
|
||||
>The socket options that Samba uses are settable both on the command
|
||||
line with the -O option, or in the smb.conf file.</P
|
||||
><P
|
||||
>The "socket options" section of the smb.conf manual page describes how
|
||||
to set these and gives recommendations.</P
|
||||
><P
|
||||
>Getting the socket options right can make a big difference to your
|
||||
performance, but getting them wrong can degrade it by just as
|
||||
much. The correct settings are very dependent on your local network.</P
|
||||
><P
|
||||
>The socket option TCP_NODELAY is the one that seems to make the
|
||||
biggest single difference for most networks. Many people report that
|
||||
adding "socket options = TCP_NODELAY" doubles the read performance of
|
||||
a Samba drive. The best explanation I have seen for this is that the
|
||||
Microsoft TCP/IP stack is slow in sending tcp ACKs.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN36"
|
||||
>Read size</A
|
||||
></H1
|
||||
><P
|
||||
>The option "read size" affects the overlap of disk reads/writes with
|
||||
network reads/writes. If the amount of data being transferred in
|
||||
several of the SMB commands (currently SMBwrite, SMBwriteX and
|
||||
SMBreadbraw) is larger than this value then the server begins writing
|
||||
the data before it has received the whole packet from the network, or
|
||||
in the case of SMBreadbraw, it begins writing to the network before
|
||||
all the data has been read from disk.</P
|
||||
><P
|
||||
>This overlapping works best when the speeds of disk and network access
|
||||
are similar, having very little effect when the speed of one is much
|
||||
greater than the other.</P
|
||||
><P
|
||||
>The default value is 16384, but very little experimentation has been
|
||||
done yet to determine the optimal value, and it is likely that the best
|
||||
value will vary greatly between systems anyway. A value over 65536 is
|
||||
pointless and will cause you to allocate memory unnecessarily.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN41"
|
||||
>Max xmit</A
|
||||
></H1
|
||||
><P
|
||||
>At startup the client and server negotiate a "maximum transmit" size,
|
||||
which limits the size of nearly all SMB commands. You can set the
|
||||
maximum size that Samba will negotiate using the "max xmit = " option
|
||||
in smb.conf. Note that this is the maximum size of SMB request that
|
||||
Samba will accept, but not the maximum size that the *client* will accept.
|
||||
The client maximum receive size is sent to Samba by the client and Samba
|
||||
honours this limit.</P
|
||||
><P
|
||||
>It defaults to 65536 bytes (the maximum), but it is possible that some
|
||||
clients may perform better with a smaller transmit unit. Trying values
|
||||
of less than 2048 is likely to cause severe problems.</P
|
||||
><P
|
||||
>In most cases the default is the best option.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN46"
|
||||
>Locking</A
|
||||
></H1
|
||||
><P
|
||||
>By default Samba does not implement strict locking on each read/write
|
||||
call (although it did in previous versions). If you enable strict
|
||||
locking (using "strict locking = yes") then you may find that you
|
||||
suffer a severe performance hit on some systems.</P
|
||||
><P
|
||||
>The performance hit will probably be greater on NFS mounted
|
||||
filesystems, but could be quite high even on local disks.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN50"
|
||||
>Share modes</A
|
||||
></H1
|
||||
><P
|
||||
>Some people find that opening files is very slow. This is often
|
||||
because of the "share modes" code needed to fully implement the dos
|
||||
share modes stuff. You can disable this code using "share modes =
|
||||
no". This will gain you a lot in opening and closing files but will
|
||||
mean that (in some cases) the system won't force a second user of a
|
||||
file to open the file read-only if the first has it open
|
||||
read-write. For many applications that do their own locking this
|
||||
doesn't matter, but for some it may. Most Windows applications
|
||||
depend heavily on "share modes" working correctly and it is
|
||||
recommended that the Samba share mode support be left at the
|
||||
default of "on".</P
|
||||
><P
|
||||
>The share mode code in Samba has been re-written in the 1.9.17
|
||||
release following tests with the Ziff-Davis NetBench PC Benchmarking
|
||||
tool. It is now believed that Samba 1.9.17 implements share modes
|
||||
similarly to Windows NT.</P
|
||||
><P
|
||||
>NOTE: In the most recent versions of Samba there is an option to use
|
||||
shared memory via mmap() to implement the share modes. This makes
|
||||
things much faster. See the Makefile for how to enable this.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN55"
|
||||
>Log level</A
|
||||
></H1
|
||||
><P
|
||||
>If you set the log level (also known as "debug level") higher than 2
|
||||
then you may suffer a large drop in performance. This is because the
|
||||
server flushes the log file after each operation, which can be very
|
||||
expensive. </P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN58"
|
||||
>Wide lines</A
|
||||
></H1
|
||||
><P
|
||||
>The "wide links" option is now enabled by default, but if you disable
|
||||
it (for better security) then you may suffer a performance hit in
|
||||
resolving filenames. The performance loss is lessened if you have
|
||||
"getwd cache = yes", which is now the default.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN61"
|
||||
>Read raw</A
|
||||
></H1
|
||||
><P
|
||||
>The "read raw" operation is designed to be an optimised, low-latency
|
||||
file read operation. A server may choose to not support it,
|
||||
however. and Samba makes support for "read raw" optional, with it
|
||||
being enabled by default.</P
|
||||
><P
|
||||
>In some cases clients don't handle "read raw" very well and actually
|
||||
get lower performance using it than they get using the conventional
|
||||
read operations. </P
|
||||
><P
|
||||
>So you might like to try "read raw = no" and see what happens on your
|
||||
network. It might lower, raise or not affect your performance. Only
|
||||
testing can really tell.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN66"
|
||||
>Write raw</A
|
||||
></H1
|
||||
><P
|
||||
>The "write raw" operation is designed to be an optimised, low-latency
|
||||
file write operation. A server may choose to not support it,
|
||||
however. and Samba makes support for "write raw" optional, with it
|
||||
being enabled by default.</P
|
||||
><P
|
||||
>Some machines may find "write raw" slower than normal write, in which
|
||||
case you may wish to change this option.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN70"
|
||||
>Read prediction</A
|
||||
></H1
|
||||
><P
|
||||
>Samba can do read prediction on some of the SMB commands. Read
|
||||
prediction means that Samba reads some extra data on the last file it
|
||||
read while waiting for the next SMB command to arrive. It can then
|
||||
respond more quickly when the next read request arrives.</P
|
||||
><P
|
||||
>This is disabled by default. You can enable it by using "read
|
||||
prediction = yes".</P
|
||||
><P
|
||||
>Note that read prediction is only used on files that were opened read
|
||||
only.</P
|
||||
><P
|
||||
>Read prediction should particularly help for those silly clients (such
|
||||
as "Write" under NT) which do lots of very small reads on a file.</P
|
||||
><P
|
||||
>Samba will not read ahead more data than the amount specified in the
|
||||
"read size" option. It always reads ahead on 1k block boundaries.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN77"
|
||||
>Memory mapping</A
|
||||
></H1
|
||||
><P
|
||||
>Samba supports reading files via memory mapping them. One some
|
||||
machines this can give a large boost to performance, on others it
|
||||
makes not difference at all, and on some it may reduce performance.</P
|
||||
><P
|
||||
>To enable you you have to recompile Samba with the -DUSE_MMAP option
|
||||
on the FLAGS line of the Makefile.</P
|
||||
><P
|
||||
>Note that memory mapping is only used on files opened read only, and
|
||||
is not used by the "read raw" operation. Thus you may find memory
|
||||
mapping is more effective if you disable "read raw" using "read raw =
|
||||
no".</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN82"
|
||||
>Slow Clients</A
|
||||
></H1
|
||||
><P
|
||||
>One person has reported that setting the protocol to COREPLUS rather
|
||||
than LANMAN2 gave a dramatic speed improvement (from 10k/s to 150k/s).</P
|
||||
><P
|
||||
>I suspect that his PC's (386sx16 based) were asking for more data than
|
||||
they could chew. I suspect a similar speed could be had by setting
|
||||
"read raw = no" and "max xmit = 2048", instead of changing the
|
||||
protocol. Lowering the "read size" might also help.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN86"
|
||||
>Slow Logins</A
|
||||
></H1
|
||||
><P
|
||||
>Slow logins are almost always due to the password checking time. Using
|
||||
the lowest practical "password level" will improve things a lot. You
|
||||
could also enable the "UFC crypt" option in the Makefile.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN89"
|
||||
>Client tuning</A
|
||||
></H1
|
||||
><P
|
||||
>Often a speed problem can be traced to the client. The client (for
|
||||
example Windows for Workgroups) can often be tuned for better TCP
|
||||
performance.</P
|
||||
><P
|
||||
>See your client docs for details. In particular, I have heard rumours
|
||||
that the WfWg options TCPWINDOWSIZE and TCPSEGMENTSIZE can have a
|
||||
large impact on performance.</P
|
||||
><P
|
||||
>Also note that some people have found that setting DefaultRcvWindow in
|
||||
the [MSTCP] section of the SYSTEM.INI file under WfWg to 3072 gives a
|
||||
big improvement. I don't know why.</P
|
||||
><P
|
||||
>My own experience wth DefaultRcvWindow is that I get much better
|
||||
performance with a large value (16384 or larger). Other people have
|
||||
reported that anything over 3072 slows things down enourmously. One
|
||||
person even reported a speed drop of a factor of 30 when he went from
|
||||
3072 to 8192. I don't know why.</P
|
||||
><P
|
||||
>It probably depends a lot on your hardware, and the type of unix box
|
||||
you have at the other end of the link.</P
|
||||
><P
|
||||
>Paul Cochrane has done some testing on client side tuning and come
|
||||
to the following conclusions:</P
|
||||
><P
|
||||
>Install the W2setup.exe file from www.microsoft.com. This is an
|
||||
update for the winsock stack and utilities which improve performance.</P
|
||||
><P
|
||||
>Configure the win95 TCPIP registry settings to give better
|
||||
perfomance. I use a program called MTUSPEED.exe which I got off the
|
||||
net. There are various other utilities of this type freely available.
|
||||
The setting which give the best performance for me are:</P
|
||||
><P
|
||||
></P
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
>MaxMTU Remove</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>RWIN Remove</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>MTUAutoDiscover Disable</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>MTUBlackHoleDetect Disable</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>Time To Live Enabled</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>Time To Live - HOPS 32</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>NDI Cache Size 0</P
|
||||
></LI
|
||||
></OL
|
||||
><P
|
||||
>I tried virtually all of the items mentioned in the document and
|
||||
the only one which made a difference to me was the socket options. It
|
||||
turned out I was better off without any!!!!!</P
|
||||
><P
|
||||
>In terms of overall speed of transfer, between various win95 clients
|
||||
and a DX2-66 20MB server with a crappy NE2000 compatible and old IDE
|
||||
drive (Kernel 2.0.30). The transfer rate was reasonable for 10 baseT.</P
|
||||
><P
|
||||
>FIXME
|
||||
The figures are: Put Get
|
||||
P166 client 3Com card: 420-440kB/s 500-520kB/s
|
||||
P100 client 3Com card: 390-410kB/s 490-510kB/s
|
||||
DX4-75 client NE2000: 370-380kB/s 330-350kB/s</P
|
||||
><P
|
||||
>I based these test on transfer two files a 4.5MB text file and a 15MB
|
||||
textfile. The results arn't bad considering the hardware Samba is
|
||||
running on. It's a crap machine!!!!</P
|
||||
><P
|
||||
>The updates mentioned in 1 and 2 brought up the transfer rates from
|
||||
just over 100kB/s in some clients.</P
|
||||
><P
|
||||
>A new client is a P333 connected via a 100MB/s card and hub. The
|
||||
transfer rates from this were good: 450-500kB/s on put and 600+kB/s
|
||||
on get.</P
|
||||
><P
|
||||
>Looking at standard FTP throughput, Samba is a bit slower (100kB/s
|
||||
upwards). I suppose there is more going on in the samba protocol, but
|
||||
if it could get up to the rate of FTP the perfomance would be quite
|
||||
staggering.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN121"
|
||||
>My Results</A
|
||||
></H1
|
||||
><P
|
||||
>Some people want to see real numbers in a document like this, so here
|
||||
they are. I have a 486sx33 client running WfWg 3.11 with the 3.11b
|
||||
tcp/ip stack. It has a slow IDE drive and 20Mb of ram. It has a SMC
|
||||
Elite-16 ISA bus ethernet card. The only WfWg tuning I've done is to
|
||||
set DefaultRcvWindow in the [MSTCP] section of system.ini to 16384. My
|
||||
server is a 486dx3-66 running Linux. It also has 20Mb of ram and a SMC
|
||||
Elite-16 card. You can see my server config in the examples/tridge/
|
||||
subdirectory of the distribution.</P
|
||||
><P
|
||||
>I get 490k/s on reading a 8Mb file with copy.
|
||||
I get 441k/s writing the same file to the samba server.</P
|
||||
><P
|
||||
>Of course, there's a lot more to benchmarks than 2 raw throughput
|
||||
figures, but it gives you a ballpark figure.</P
|
||||
><P
|
||||
>I've also tested Win95 and WinNT, and found WinNT gave me the best
|
||||
speed as a samba client. The fastest client of all (for me) is
|
||||
smbclient running on another linux box. Maybe I'll add those results
|
||||
here someday ...</P
|
||||
></DIV
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
169
docs/htmldocs/security_level.html
Normal file
169
docs/htmldocs/security_level.html
Normal file
@ -0,0 +1,169 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Security levels</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD
|
||||
><BODY
|
||||
CLASS="ARTICLE"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="ARTICLE"
|
||||
><DIV
|
||||
CLASS="TITLEPAGE"
|
||||
><H1
|
||||
CLASS="TITLE"
|
||||
><A
|
||||
NAME="SECURITY_LEVELS"
|
||||
>Security levels</A
|
||||
></H1
|
||||
><HR></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN3"
|
||||
>Introduction</A
|
||||
></H1
|
||||
><P
|
||||
>Samba supports the following options to the global smb.conf parameter</P
|
||||
><P
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>[global]
|
||||
<A
|
||||
HREF="smb.conf.5.html#SECURITY"
|
||||
TARGET="_top"
|
||||
><TT
|
||||
CLASS="PARAMETER"
|
||||
><I
|
||||
>security</I
|
||||
></TT
|
||||
></A
|
||||
> = [share|user(default)|domain|ads]</PRE
|
||||
></P
|
||||
><P
|
||||
>Please refer to the smb.conf man page for usage information and to the document
|
||||
<A
|
||||
HREF="DOMAIN_MEMBER.html"
|
||||
TARGET="_top"
|
||||
>DOMAIN_MEMBER.html</A
|
||||
> for further background details
|
||||
on domain mode security. The Windows 2000 Kerberos domain security model
|
||||
(security = ads) is described in the <A
|
||||
HREF="ADS-HOWTO.html"
|
||||
TARGET="_top"
|
||||
>ADS-HOWTO.html</A
|
||||
>.</P
|
||||
><P
|
||||
>Of the above, "security = server" means that Samba reports to clients that
|
||||
it is running in "user mode" but actually passes off all authentication
|
||||
requests to another "user mode" server. This requires an additional
|
||||
parameter "password server =" that points to the real authentication server.
|
||||
That real authentication server can be another Samba server or can be a
|
||||
Windows NT server, the later natively capable of encrypted password support.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECT1"
|
||||
><HR><H1
|
||||
CLASS="SECT1"
|
||||
><A
|
||||
NAME="AEN14"
|
||||
>More complete description of security levels</A
|
||||
></H1
|
||||
><P
|
||||
>A SMB server tells the client at startup what "security level" it is
|
||||
running. There are two options "share level" and "user level". Which
|
||||
of these two the client receives affects the way the client then tries
|
||||
to authenticate itself. It does not directly affect (to any great
|
||||
extent) the way the Samba server does security. I know this is
|
||||
strange, but it fits in with the client/server approach of SMB. In SMB
|
||||
everything is initiated and controlled by the client, and the server
|
||||
can only tell the client what is available and whether an action is
|
||||
allowed. </P
|
||||
><P
|
||||
>I'll describe user level security first, as its simpler. In user level
|
||||
security the client will send a "session setup" command directly after
|
||||
the protocol negotiation. This contains a username and password. The
|
||||
server can either accept or reject that username/password
|
||||
combination. Note that at this stage the server has no idea what
|
||||
share the client will eventually try to connect to, so it can't base
|
||||
the "accept/reject" on anything other than:</P
|
||||
><P
|
||||
></P
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
>the username/password</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>the machine that the client is coming from</P
|
||||
></LI
|
||||
></OL
|
||||
><P
|
||||
>If the server accepts the username/password then the client expects to
|
||||
be able to mount any share (using a "tree connection") without
|
||||
specifying a password. It expects that all access rights will be as
|
||||
the username/password specified in the "session setup". </P
|
||||
><P
|
||||
>It is also possible for a client to send multiple "session setup"
|
||||
requests. When the server responds it gives the client a "uid" to use
|
||||
as an authentication tag for that username/password. The client can
|
||||
maintain multiple authentication contexts in this way (WinDD is an
|
||||
example of an application that does this)</P
|
||||
><P
|
||||
>Ok, now for share level security. In share level security the client
|
||||
authenticates itself separately for each share. It will send a
|
||||
password along with each "tree connection" (share mount). It does not
|
||||
explicitly send a username with this operation. The client is
|
||||
expecting a password to be associated with each share, independent of
|
||||
the user. This means that samba has to work out what username the
|
||||
client probably wants to use. It is never explicitly sent the
|
||||
username. Some commercial SMB servers such as NT actually associate
|
||||
passwords directly with shares in share level security, but samba
|
||||
always uses the unix authentication scheme where it is a
|
||||
username/password that is authenticated, not a "share/password".</P
|
||||
><P
|
||||
>Many clients send a "session setup" even if the server is in share
|
||||
level security. They normally send a valid username but no
|
||||
password. Samba records this username in a list of "possible
|
||||
usernames". When the client then does a "tree connection" it also adds
|
||||
to this list the name of the share they try to connect to (useful for
|
||||
home directories) and any users listed in the "user =" smb.conf
|
||||
line. The password is then checked in turn against these "possible
|
||||
usernames". If a match is found then the client is authenticated as
|
||||
that user.</P
|
||||
><P
|
||||
>Finally "server level" security. In server level security the samba
|
||||
server reports to the client that it is in user level security. The
|
||||
client then does a "session setup" as described earlier. The samba
|
||||
server takes the username/password that the client sends and attempts
|
||||
to login to the "password server" by sending exactly the same
|
||||
username/password that it got from the client. If that server is in
|
||||
user level security and accepts the password then samba accepts the
|
||||
clients connection. This allows the samba server to use another SMB
|
||||
server as the "password server". </P
|
||||
><P
|
||||
>You should also note that at the very start of all this, where the
|
||||
server tells the client what security level it is in, it also tells
|
||||
the client if it supports encryption. If it does then it supplies the
|
||||
client with a random "cryptkey". The client will then send all
|
||||
passwords in encrypted form. You have to compile samba with encryption
|
||||
enabled to support this feature, and you have to maintain a separate
|
||||
smbpasswd file with SMB style encrypted passwords. It is
|
||||
cryptographically impossible to translate from unix style encryption
|
||||
to SMB style encryption, although there are some fairly simple management
|
||||
schemes by which the two could be kept in sync.</P
|
||||
></DIV
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
42
examples/VFS/Makefile.in
Normal file
42
examples/VFS/Makefile.in
Normal file
@ -0,0 +1,42 @@
|
||||
MAKEFILE = Makefile.vfs
|
||||
|
||||
include $(MAKEFILE)
|
||||
|
||||
CC = @CC@
|
||||
LIBTOOL = libtool
|
||||
CFLAGS = @CFLAGS@ $(VFS_CFLAGS)
|
||||
CPPFLAGS = @CPPFLAGS@ $(VFS_CPPFLAGS)
|
||||
LDFLAGS = @LDFLAGS@ $(VFS_LDFLAGS)
|
||||
LDSHFLAGS = -shared
|
||||
srcdir = @builddir@
|
||||
FLAGS = $(CFLAGS) -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I$(srcdir)/smbwrapper -I. $(CPPFLAGS) -I$(srcdir)
|
||||
|
||||
# Default target
|
||||
|
||||
default: $(VFS_OBJS)
|
||||
|
||||
# if file doesn't exist try to create one;
|
||||
# it is possible that some variables will be
|
||||
# defined correctly
|
||||
Makefile.vfs:
|
||||
@echo -ne "VFS_OBJS\t= " > $(MAKEFILE); \
|
||||
for i in *.c; do \
|
||||
echo -n $$i" " | sed -e 's/\(.*\)\.c\(.*\)/\1\.so\2/g' >> $(MAKEFILE); \
|
||||
done; \
|
||||
echo -ne "\nVFS_CFLAGS\t= \nVFS_CPPFLAGS\t= \nVFS_LDFLAGS\t= \n" >> $(MAKEFILE)
|
||||
make
|
||||
|
||||
# Pattern rules
|
||||
|
||||
%.so: %.lo
|
||||
$(LIBTOOL) $(CC) $(LDSHFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
%.lo: %.c
|
||||
$(LIBTOOL) $(CC) $(FLAGS) -c $<
|
||||
|
||||
# Misc targets
|
||||
|
||||
clean:
|
||||
rm -rf .libs
|
||||
rm -f core *~ *% *.bak \
|
||||
$(VFS_OBJS) $(VFS_OBJS:.so=.o) $(VFS_OBJS:.so=.lo)
|
42
examples/VFS/block/Makefile.in
Normal file
42
examples/VFS/block/Makefile.in
Normal file
@ -0,0 +1,42 @@
|
||||
MAKEFILE = Makefile.vfs
|
||||
|
||||
include $(MAKEFILE)
|
||||
|
||||
CC = @CC@
|
||||
LIBTOOL = libtool
|
||||
CFLAGS = @CFLAGS@ $(VFS_CFLAGS)
|
||||
CPPFLAGS = @CPPFLAGS@ $(VFS_CPPFLAGS)
|
||||
LDFLAGS = @LDFLAGS@ $(VFS_LDFLAGS)
|
||||
LDSHFLAGS = -shared
|
||||
srcdir = @builddir@
|
||||
FLAGS = $(CFLAGS) -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I$(srcdir)/smbwrapper -I. $(CPPFLAGS) -I$(srcdir)
|
||||
|
||||
# Default target
|
||||
|
||||
default: $(VFS_OBJS)
|
||||
|
||||
# if file doesn't exist try to create one;
|
||||
# it is possible that some variables will be
|
||||
# defined correctly
|
||||
Makefile.vfs:
|
||||
@echo -ne "VFS_OBJS\t= " > $(MAKEFILE); \
|
||||
for i in *.c; do \
|
||||
echo -n $$i" " | sed -e 's/\(.*\)\.c\(.*\)/\1\.so\2/g' >> $(MAKEFILE); \
|
||||
done; \
|
||||
echo -ne "\nVFS_CFLAGS\t= \nVFS_CPPFLAGS\t= \nVFS_LDFLAGS\t= \n" >> $(MAKEFILE)
|
||||
make
|
||||
|
||||
# Pattern rules
|
||||
|
||||
%.so: %.lo
|
||||
$(LIBTOOL) $(CC) $(LDSHFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
%.lo: %.c
|
||||
$(LIBTOOL) $(CC) $(FLAGS) -c $<
|
||||
|
||||
# Misc targets
|
||||
|
||||
clean:
|
||||
rm -rf .libs
|
||||
rm -f core *~ *% *.bak \
|
||||
$(VFS_OBJS) $(VFS_OBJS:.so=.o) $(VFS_OBJS:.so=.lo)
|
154
examples/misc/modify_samba_config.pl
Executable file
154
examples/misc/modify_samba_config.pl
Executable file
@ -0,0 +1,154 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
##
|
||||
## Simple example of how to implement a '[add|delete] share command' for
|
||||
## use with the Windows NT Server Manager. See smb.conf(5) for details
|
||||
## on the '[add|delete] share command'
|
||||
##
|
||||
## Author : Gerald (Jerry) Carter <jerry@samba.org>
|
||||
##
|
||||
|
||||
use POSIX qw(tmpnam);
|
||||
|
||||
##
|
||||
## local variables
|
||||
##
|
||||
my $delete_mode = undef;
|
||||
my $add_mode = undef;
|
||||
my $tmp_file_name = undef;
|
||||
|
||||
|
||||
## check for correct parameters
|
||||
if ($#ARGV == 1) {
|
||||
$delete_mode = 1;
|
||||
}
|
||||
elsif ($#ARGV == 3) {
|
||||
$add_mode = 1;
|
||||
}
|
||||
else {
|
||||
print "Usage: $0 configfile share [path] [comment]\n";
|
||||
exit -1;
|
||||
}
|
||||
|
||||
## first param is always the config file
|
||||
open (CONFIGFILE, "$ARGV[0]") || die "Unable to open $ARGV[0] for reading!\n";
|
||||
|
||||
## FIXME!! Right now we throw away all comments in the file.
|
||||
while (<CONFIGFILE>) {
|
||||
|
||||
chomp($_);
|
||||
|
||||
## eat leading whitespace
|
||||
$_ =~ s/^\s*//;
|
||||
|
||||
## eat trailing whitespace
|
||||
$_ =~ s/\s*$//;
|
||||
|
||||
|
||||
## throw away comments
|
||||
next if (($_ =~ /^#/) || ($_ =~ /^;/));
|
||||
|
||||
## set the current section name for storing the hash
|
||||
if ($_ =~ /^\[.*\]$/) {
|
||||
|
||||
$_ = substr($_, 1, length($_)-2);
|
||||
|
||||
if ( length($_) ) {
|
||||
$section = $_;
|
||||
}
|
||||
else {
|
||||
print "Bad Section Name - no closing ]\n";
|
||||
exit -1;
|
||||
}
|
||||
|
||||
next;
|
||||
}
|
||||
|
||||
## check for a param = value
|
||||
if ($_ =~ /=/) {
|
||||
($param, $value) = split (/=/, $_);
|
||||
$param =~ s/./\l$&/g;
|
||||
$param =~ s/\s+//g;
|
||||
$value =~ s/^\s+//;
|
||||
|
||||
$config{$section}{$param} = $value;
|
||||
|
||||
next;
|
||||
}
|
||||
|
||||
## should have a hash of hashes indexed by section name
|
||||
}
|
||||
close (CONFIGFILE);
|
||||
|
||||
##
|
||||
## We have the smb.conf in our hash of hashes now.
|
||||
## Add or delete
|
||||
##
|
||||
if ($add_mode) {
|
||||
$config{$ARGV[1]}{'path'} = $ARGV[2];
|
||||
$config{$ARGV[1]}{'comment'} = $ARGV[3];
|
||||
}
|
||||
elsif ($delete_mode) {
|
||||
delete $config{$ARGV[1]};
|
||||
}
|
||||
|
||||
##
|
||||
## Print the resulting configuration
|
||||
##
|
||||
#do {
|
||||
# $tmp_file_name = tmpnam();
|
||||
# print "Using temporary file - $tmp_file_name\n";
|
||||
#} while (!sysopen(TMP, $tmp_file_name, O_RDWR|O_CREAT|O_EXCL));
|
||||
$tmp_file_name = tmpnam();
|
||||
open (TMP, ">$tmp_file_name") || die "Unable to open temporary file for writing!\n";
|
||||
|
||||
PrintConfigFile(TMP);
|
||||
|
||||
## now overwrite the original config file
|
||||
close (TMP);
|
||||
system ("cp -pf $ARGV[0] $ARGV[0].bak");
|
||||
system ("cp -pf $tmp_file_name $ARGV[0]");
|
||||
unlink $tmp_file_name;
|
||||
|
||||
|
||||
exit 0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#######################################################################################
|
||||
## PrintConfigFile()
|
||||
##
|
||||
sub PrintConfigFile {
|
||||
my ($output) = @_;
|
||||
|
||||
## print the file back out, beginning with the global section
|
||||
print $output "#\n# Generated by $0\n#\n";
|
||||
|
||||
PrintSection ($output, 'global', $config{'global'});
|
||||
|
||||
foreach $section (keys %config) {
|
||||
|
||||
if ("$section" ne "global") {
|
||||
print $output "## Section - [$section]\n";
|
||||
PrintSection ($output, $section, $config{$section});
|
||||
}
|
||||
}
|
||||
|
||||
print $output "#\n# end of generated smb.conf\n#\n";
|
||||
}
|
||||
|
||||
#######################################################################################
|
||||
## PrintSection()
|
||||
##
|
||||
sub PrintSection {
|
||||
my ($outfile, $name, $section) = @_;
|
||||
|
||||
print $outfile "[$name]\n";
|
||||
foreach $param (keys %$section) {
|
||||
print $outfile "\t$param".' 'x(25-length($param)). " = $$section{$param}\n";
|
||||
}
|
||||
print $outfile "\n";
|
||||
|
||||
}
|
41
examples/pdb/mysql/ChangeLog
Normal file
41
examples/pdb/mysql/ChangeLog
Normal file
@ -0,0 +1,41 @@
|
||||
** This file is now deprecated, use CVS' log featues **
|
||||
|
||||
2002-06-13 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Converted to using SID's like samba HEAD does now
|
||||
* Fixed some FIXME's
|
||||
|
||||
2002-05-28 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Updated docs, after some testing by Vance Lankhaar
|
||||
|
||||
2002-05-25 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Added support for dynamic debug classes
|
||||
* Fixed nt/lanman password support
|
||||
* Released 1.2
|
||||
|
||||
2002-05-06 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Added support for multiple instances of pdb_mysql
|
||||
* Added identifiers
|
||||
* Updated documentation
|
||||
* Released 1.1
|
||||
|
||||
2002-04-27 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Updated documentation
|
||||
* Released 1.0!
|
||||
|
||||
2002-04-27 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Added update/add sam account support
|
||||
* Released 0.4
|
||||
|
||||
2002-04-13 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Support for multiple instances of pdb_mysql
|
||||
* Released 0.3
|
||||
|
||||
2002-04-12 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Now using lp_parm_string to retrieve configuration values (instead of
|
||||
our configuration files)
|
||||
* Updated documentation
|
||||
* Released 0.2
|
||||
|
||||
2002-04-10 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Released 0.1
|
||||
* Initial release. Not supporting adding and updating data of users
|
33
examples/pdb/mysql/Makefile.in
Normal file
33
examples/pdb/mysql/Makefile.in
Normal file
@ -0,0 +1,33 @@
|
||||
PDB_OBJS = pdb_mysql.so
|
||||
PDB_LDFLAGS = -lmysqlclient
|
||||
MAKEFILE = Makefile.pdb
|
||||
|
||||
include $(MAKEFILE)
|
||||
|
||||
CC = @CC@
|
||||
LIBTOOL = libtool
|
||||
CFLAGS = @CFLAGS@ $(PDB_CFLAGS)
|
||||
CPPFLAGS = @CPPFLAGS@ $(PDB_CPPFLAGS)
|
||||
LDFLAGS = @LDFLAGS@ $(PDB_LDFLAGS)
|
||||
LDSHFLAGS = -shared
|
||||
srcdir = @builddir@
|
||||
FLAGS = $(CFLAGS) -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I$(srcdir)/smbwrapper -I. $(CPPFLAGS) -I$(srcdir)
|
||||
|
||||
# Default target
|
||||
|
||||
default: $(PDB_OBJS)
|
||||
|
||||
# Pattern rules
|
||||
|
||||
%.so: %.lo
|
||||
$(LIBTOOL) $(CC) $(LDSHFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
%.lo: %.c
|
||||
$(LIBTOOL) $(CC) $(FLAGS) -c $<
|
||||
|
||||
# Misc targets
|
||||
|
||||
clean:
|
||||
rm -rf .libs
|
||||
rm -f core *~ *% *.bak \
|
||||
$(PDB_OBJS) $(PDB_OBJS:.so=.o) $(PDB_OBJS:.so=.lo)
|
92
examples/pdb/mysql/README
Normal file
92
examples/pdb/mysql/README
Normal file
@ -0,0 +1,92 @@
|
||||
PDB MySQL plugin for samba v1.1
|
||||
--
|
||||
|
||||
Building
|
||||
=========
|
||||
Before you can build the plugin, set the variable SAMBA_SRC in Makefile to the
|
||||
path containing the samba sources. This is usually the 'source' directory in
|
||||
the samba tarball or CVS.
|
||||
|
||||
Next, type make, and then copy pdb_mysql.so to any location you want. I
|
||||
strongly recommend installing it in $PREFIX/lib or /usr/lib/samba/
|
||||
|
||||
Configuring
|
||||
============
|
||||
This plugin lacks some good documentation, but here is some short info:
|
||||
|
||||
Add a the following to the 'passdb backend' variable in your smb.conf:
|
||||
|
||||
passdb backend = [other-plugins] plugin:/location/to/pdb_mysql.so:identifier [other-plugins]
|
||||
|
||||
The identifier can be any string you like, as long as it doesn't collide with
|
||||
the identifiers of other plugins or other instances of pdb_mysql. If you
|
||||
specify multiple pdb_mysql.so entries in 'passdb backend', you also need to
|
||||
use different identifiers!
|
||||
|
||||
Additional options can be given thru the smb.conf file in the [global] section.
|
||||
|
||||
identifier:mysql host - host name, defaults to 'localhost'
|
||||
identifier:mysql password
|
||||
identifier:mysql user - defaults to 'samba'
|
||||
identifier:mysql database - defaults to 'samba'
|
||||
identifier:mysql port - defaults to 3306
|
||||
identifier:table - Name of the table containing users
|
||||
|
||||
Names of the columns in this table(I've added column types those columns
|
||||
should have first):
|
||||
identifier:logon time column - int(9)
|
||||
identifier:logoff time column - int(9)
|
||||
identifier:kickoff time column - int(9)
|
||||
identifier:pass last set time column - int(9)
|
||||
identifier:pass can change time column - int(9)
|
||||
identifier:pass must change time column - int(9)
|
||||
identifier:username column - varchar(255) - unix username
|
||||
identifier:domain column - varchar(255) - NT domain user is part of
|
||||
identifier:nt username column - varchar(255) - NT username
|
||||
identifier:fullname column - varchar(255) - Full name of user
|
||||
identifier:home dir column - varchar(255) - Unix homedir path
|
||||
identifier:dir drive column - varchar(2) - Directory drive path (eg: 'H:')
|
||||
identifier:logon script column - varchar(255) - Batch file to run on client side when logging on
|
||||
identifier:profile path column - varchar(255) - Path of profile
|
||||
identifier:acct desc column - varchar(255) - Some ASCII NT user data
|
||||
identifier:workstations column - varchar(255) - Workstations user can logon to (or NULL for all)
|
||||
identifier:unknown string column - varchar(255) - unknown string
|
||||
identifier:munged dial column - varchar(255) - ?
|
||||
identifier:uid column - int(9) - Unix user ID (uid)
|
||||
identifier:gid column - int(9) - Unix user group (gid)
|
||||
identifier:user sid column - varchar(255) - NT user SID
|
||||
identifier:group sid column - varchar(255) - NT group ID
|
||||
identifier:lanman pass column - varchar(255) - encrypted lanman password
|
||||
identifier:nt pass column - varchar(255) - encrypted nt passwd
|
||||
identifier:plaintext pass column - varchar(255) - plaintext password
|
||||
identifier:acct control column - int(9) - nt user data
|
||||
identifier:unknown 3 column - int(9) - unknown
|
||||
identifier:logon divs column - int(9) - ?
|
||||
identifier:hours len column - int(9) - ?
|
||||
identifier:unknown 5 column - int(9) - unknown
|
||||
identifier:unknown 6 column - int(9) - unknown
|
||||
|
||||
Eventually, you can put a colon (:) after the name of each column, which
|
||||
should specify the column to update when updating the table. You can also
|
||||
specify nothing behind the colon - then the data from the field will not be
|
||||
updated.
|
||||
|
||||
Using plaintext passwords or encrypted password
|
||||
===============================================
|
||||
I strongly discourage the use of plaintext passwords, however, you can use them:
|
||||
|
||||
If you would like to use plaintext passwords, set 'identifier:lanman pass column' and 'identifier:nt pass column' to 'NULL' (without the quotes) and 'identifier:plaintext pass column' to the name of the column containing the plaintext passwords.
|
||||
|
||||
If you use encrypted passwords, set the 'identifier:plaintext pass column' to 'NULL' (without the quotes). This is the default.
|
||||
|
||||
Getting non-column data from the table
|
||||
======================================
|
||||
It is possible to have not all data in the database and making some 'constant'.
|
||||
|
||||
For example, you can set 'identifier:fullname column' to :
|
||||
CONCAT(First_name,' ',Sur_name)
|
||||
|
||||
Or, set 'identifier:workstations column' to :
|
||||
NULL
|
||||
|
||||
See the MySQL documentation for more language constructs.
|
983
examples/pdb/mysql/pdb_mysql.c
Normal file
983
examples/pdb/mysql/pdb_mysql.c
Normal file
@ -0,0 +1,983 @@
|
||||
|
||||
/*
|
||||
* MySQL password backend for samba
|
||||
* Copyright (C) Jelmer Vernooij 2002
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 675
|
||||
* Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
#define CONFIG_TABLE_DEFAULT "user"
|
||||
#define CONFIG_LOGON_TIME_DEFAULT "logon_time"
|
||||
#define CONFIG_LOGOFF_TIME_DEFAULT "logoff_time"
|
||||
#define CONFIG_KICKOFF_TIME_DEFAULT "kickoff_time"
|
||||
#define CONFIG_PASS_LAST_SET_TIME_DEFAULT "pass_last_set_time"
|
||||
#define CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT "pass_can_change_time"
|
||||
#define CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT "pass_must_change_time"
|
||||
#define CONFIG_USERNAME_DEFAULT "username"
|
||||
#define CONFIG_DOMAIN_DEFAULT "domain"
|
||||
#define CONFIG_NT_USERNAME_DEFAULT "nt_username"
|
||||
#define CONFIG_FULLNAME_DEFAULT "nt_fullname"
|
||||
#define CONFIG_HOME_DIR_DEFAULT "home_dir"
|
||||
#define CONFIG_DIR_DRIVE_DEFAULT "dir_drive"
|
||||
#define CONFIG_LOGON_SCRIPT_DEFAULT "logon_script"
|
||||
#define CONFIG_PROFILE_PATH_DEFAULT "profile_path"
|
||||
#define CONFIG_ACCT_DESC_DEFAULT "acct_desc"
|
||||
#define CONFIG_WORKSTATIONS_DEFAULT "workstations"
|
||||
#define CONFIG_UNKNOWN_STR_DEFAULT "unknown_str"
|
||||
#define CONFIG_MUNGED_DIAL_DEFAULT "munged_dial"
|
||||
#define CONFIG_UID_DEFAULT "uid"
|
||||
#define CONFIG_GID_DEFAULT "gid"
|
||||
#define CONFIG_USER_SID_DEFAULT "user_sid"
|
||||
#define CONFIG_GROUP_SID_DEFAULT "group_sid"
|
||||
#define CONFIG_LM_PW_DEFAULT "lm_pw"
|
||||
#define CONFIG_NT_PW_DEFAULT "nt_pw"
|
||||
#define CONFIG_PLAIN_PW_DEFAULT "NULL"
|
||||
#define CONFIG_ACCT_CTRL_DEFAULT "acct_ctrl"
|
||||
#define CONFIG_UNKNOWN_3_DEFAULT "unknown_3"
|
||||
#define CONFIG_LOGON_DIVS_DEFAULT "logon_divs"
|
||||
#define CONFIG_HOURS_LEN_DEFAULT "hours_len"
|
||||
#define CONFIG_UNKNOWN_5_DEFAULT "unknown_5"
|
||||
#define CONFIG_UNKNOWN_6_DEFAULT "unknown_6"
|
||||
#define CONFIG_HOST_DEFAULT "localhost"
|
||||
#define CONFIG_USER_DEFAULT "samba"
|
||||
#define CONFIG_PASS_DEFAULT ""
|
||||
#define CONFIG_PORT_DEFAULT "3306"
|
||||
#define CONFIG_DB_DEFAULT "samba"
|
||||
|
||||
static int mysqlsam_debug_level = DBGC_ALL;
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS mysqlsam_debug_level
|
||||
|
||||
PDB_MODULE_VERSIONING_MAGIC
|
||||
|
||||
typedef struct pdb_mysql_data {
|
||||
MYSQL *handle;
|
||||
MYSQL_RES *pwent;
|
||||
char *location;
|
||||
} pdb_mysql_data;
|
||||
|
||||
/* Used to construct insert and update queries */
|
||||
|
||||
typedef struct pdb_mysql_query {
|
||||
char update;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
char *part1;
|
||||
char *part2;
|
||||
} pdb_mysql_query;
|
||||
|
||||
#define SET_DATA(data,methods) { \
|
||||
if(!methods){ \
|
||||
DEBUG(0, ("invalid methods!\n")); \
|
||||
return False; \
|
||||
} \
|
||||
data = (struct pdb_mysql_data *)methods->private_data; \
|
||||
if(!data || !(data->handle)){ \
|
||||
DEBUG(0, ("invalid handle!\n")); \
|
||||
return False; \
|
||||
} \
|
||||
}
|
||||
void
|
||||
pdb_mysql_int_field(struct pdb_methods *m,
|
||||
struct pdb_mysql_query *q, char *name, int value)
|
||||
{
|
||||
if (!name || strchr(name, '\''))
|
||||
return; /* This field shouldn't be set by us */
|
||||
|
||||
if (q->update) {
|
||||
q->part1 =
|
||||
talloc_asprintf_append(q->mem_ctx, q->part1,
|
||||
"%s = %d,", name, value);
|
||||
} else {
|
||||
q->part1 =
|
||||
talloc_asprintf_append(q->mem_ctx, q->part1, "%s,", name);
|
||||
q->part2 =
|
||||
talloc_asprintf_append(q->mem_ctx, q->part2, "%d,", value);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL
|
||||
pdb_mysql_string_field(struct pdb_methods *methods,
|
||||
struct pdb_mysql_query *q,
|
||||
char *name, const char *value)
|
||||
{
|
||||
char *esc_value;
|
||||
struct pdb_mysql_data *data;
|
||||
|
||||
SET_DATA(data, methods);
|
||||
|
||||
if (!name || !value || !strcmp(value, "") || strchr(name, '\''))
|
||||
return False; /* This field shouldn't be set by module */
|
||||
|
||||
esc_value = malloc(strlen(value) * 2 + 1);
|
||||
mysql_real_escape_string(data->handle, esc_value, (char *) value,
|
||||
strlen(value));
|
||||
|
||||
if (q->update) {
|
||||
q->part1 =
|
||||
talloc_asprintf_append(q->mem_ctx, q->part1,
|
||||
"%s = '%s',", name, esc_value);
|
||||
} else {
|
||||
q->part1 =
|
||||
talloc_asprintf_append(q->mem_ctx, q->part1, "%s,", name);
|
||||
q->part2 =
|
||||
talloc_asprintf_append(q->mem_ctx, q->part2, "'%s',",
|
||||
esc_value);
|
||||
}
|
||||
|
||||
SAFE_FREE(esc_value);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static char *
|
||||
config_value(pdb_mysql_data * data, char *name, char *default_value)
|
||||
{
|
||||
if (lp_parm_string(NULL, data->location, name))
|
||||
return lp_parm_string(NULL, data->location, name);
|
||||
|
||||
return default_value;
|
||||
}
|
||||
|
||||
static char *
|
||||
config_value_write(pdb_mysql_data * data, char *name, char *default_value)
|
||||
{
|
||||
char *v = config_value(data, name, NULL);
|
||||
char *write;
|
||||
|
||||
if (!v)
|
||||
return default_value;
|
||||
|
||||
write = strchr(v, ':');
|
||||
|
||||
/* Default to the same field as read field */
|
||||
if (!write)
|
||||
return v;
|
||||
|
||||
write++;
|
||||
|
||||
/* If the field is 0 chars long, we shouldn't write to it */
|
||||
if (!strlen(write) || !strcmp(write, "NULL"))
|
||||
return NULL;
|
||||
|
||||
/* Otherwise, use the additionally specified */
|
||||
return write;
|
||||
}
|
||||
|
||||
static const char *
|
||||
config_value_read(pdb_mysql_data * data, char *name, char *default_value)
|
||||
{
|
||||
char *v = config_value(data, name, NULL);
|
||||
char *write;
|
||||
|
||||
if (!v)
|
||||
return default_value;
|
||||
|
||||
write = strchr(v, ':');
|
||||
|
||||
/* If no write is specified, there are no problems */
|
||||
if (!write) {
|
||||
if (strlen(v) == 0)
|
||||
return "NULL";
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Otherwise, we have to cut the ':write_part' */
|
||||
*write = '\0';
|
||||
if (strlen(v) == 0)
|
||||
return "NULL";
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Wrapper for atol that returns 0 if 'a' points to NULL */
|
||||
static long
|
||||
xatol(char *a)
|
||||
{
|
||||
long ret = 0;
|
||||
|
||||
if (a != NULL)
|
||||
ret = atol(a);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
row_to_sam_account(MYSQL_RES * r, SAM_ACCOUNT * u)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
pstring temp;
|
||||
unsigned int num_fields;
|
||||
unsigned long *lengths;
|
||||
DOM_SID sid;
|
||||
|
||||
num_fields = mysql_num_fields(r);
|
||||
row = mysql_fetch_row(r);
|
||||
if (!row)
|
||||
return False;
|
||||
|
||||
pdb_set_logon_time(u, xatol(row[0]), FALSE);
|
||||
pdb_set_logoff_time(u, xatol(row[1]), FALSE);
|
||||
pdb_set_kickoff_time(u, xatol(row[2]), FALSE);
|
||||
pdb_set_pass_last_set_time(u, xatol(row[3]));
|
||||
pdb_set_pass_can_change_time(u, xatol(row[4]), FALSE);
|
||||
pdb_set_pass_must_change_time(u, xatol(row[5]), FALSE);
|
||||
pdb_set_username(u, row[6]);
|
||||
pdb_set_domain(u, row[7]);
|
||||
pdb_set_nt_username(u, row[8]);
|
||||
pdb_set_fullname(u, row[9]);
|
||||
pdb_set_homedir(u, row[10], True);
|
||||
pdb_set_dir_drive(u, row[11], True);
|
||||
pdb_set_logon_script(u, row[12], True);
|
||||
pdb_set_profile_path(u, row[13], True);
|
||||
pdb_set_acct_desc(u, row[14]);
|
||||
pdb_set_workstations(u, row[15]);
|
||||
pdb_set_unknown_str(u, row[16]);
|
||||
pdb_set_munged_dial(u, row[17]);
|
||||
|
||||
if (row[18])
|
||||
pdb_set_uid(u, xatol(row[18]));
|
||||
if (row[19])
|
||||
pdb_set_gid(u, xatol(row[19]));
|
||||
|
||||
string_to_sid(&sid, row[20]);
|
||||
pdb_set_user_sid(u, &sid);
|
||||
string_to_sid(&sid, row[21]);
|
||||
pdb_set_group_sid(u, &sid);
|
||||
|
||||
if (pdb_gethexpwd(row[22], temp))
|
||||
pdb_set_lanman_passwd(u, temp);
|
||||
if (pdb_gethexpwd(row[23], temp))
|
||||
pdb_set_nt_passwd(u, temp);
|
||||
|
||||
/* Only use plaintext password storage when lanman and nt are
|
||||
* NOT used */
|
||||
if (!row[22] || !row[23])
|
||||
pdb_set_plaintext_passwd(u, row[24]);
|
||||
|
||||
pdb_set_acct_ctrl(u, xatol(row[25]));
|
||||
pdb_set_unknown_3(u, xatol(row[26]));
|
||||
pdb_set_logon_divs(u, xatol(row[27]));
|
||||
pdb_set_hours_len(u, xatol(row[28]));
|
||||
pdb_set_unknown_5(u, xatol(row[29]));
|
||||
pdb_set_unknown_6(u, xatol(row[30]));
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
mysqlsam_setsampwent(struct pdb_methods *methods, BOOL update)
|
||||
{
|
||||
struct pdb_mysql_data *data =
|
||||
(struct pdb_mysql_data *) methods->private_data;
|
||||
char *query;
|
||||
int ret;
|
||||
|
||||
if (!data || !(data->handle)) {
|
||||
DEBUG(0, ("invalid handle!\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
asprintf(&query,
|
||||
"SELECT %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s FROM %s",
|
||||
config_value_read(data, "logon time column",
|
||||
CONFIG_LOGON_TIME_DEFAULT),
|
||||
config_value_read(data, "logoff time column",
|
||||
CONFIG_LOGOFF_TIME_DEFAULT),
|
||||
config_value_read(data, "kickoff time column",
|
||||
CONFIG_KICKOFF_TIME_DEFAULT),
|
||||
config_value_read(data, "pass last set time column",
|
||||
CONFIG_PASS_LAST_SET_TIME_DEFAULT),
|
||||
config_value_read(data, "pass can change time column",
|
||||
CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT),
|
||||
config_value_read(data, "pass must change time column",
|
||||
CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT),
|
||||
config_value_read(data, "username column",
|
||||
CONFIG_USERNAME_DEFAULT),
|
||||
config_value_read(data, "domain column",
|
||||
CONFIG_DOMAIN_DEFAULT),
|
||||
config_value_read(data, "nt username column",
|
||||
CONFIG_NT_USERNAME_DEFAULT),
|
||||
config_value_read(data, "fullname column",
|
||||
CONFIG_FULLNAME_DEFAULT),
|
||||
config_value_read(data, "home dir column",
|
||||
CONFIG_HOME_DIR_DEFAULT),
|
||||
config_value_read(data, "dir drive column",
|
||||
CONFIG_DIR_DRIVE_DEFAULT),
|
||||
config_value_read(data, "logon script column",
|
||||
CONFIG_LOGON_SCRIPT_DEFAULT),
|
||||
config_value_read(data, "profile path column",
|
||||
CONFIG_PROFILE_PATH_DEFAULT),
|
||||
config_value_read(data, "acct desc column",
|
||||
CONFIG_ACCT_DESC_DEFAULT),
|
||||
config_value_read(data, "workstations column",
|
||||
CONFIG_WORKSTATIONS_DEFAULT),
|
||||
config_value_read(data, "unknown string column",
|
||||
CONFIG_UNKNOWN_STR_DEFAULT),
|
||||
config_value_read(data, "munged dial column",
|
||||
CONFIG_MUNGED_DIAL_DEFAULT),
|
||||
config_value_read(data, "uid column", CONFIG_UID_DEFAULT),
|
||||
config_value_read(data, "gid column", CONFIG_GID_DEFAULT),
|
||||
config_value_read(data, "user sid column",
|
||||
CONFIG_USER_SID_DEFAULT),
|
||||
config_value_read(data, "group sid column",
|
||||
CONFIG_GROUP_SID_DEFAULT),
|
||||
config_value_read(data, "lanman pass column",
|
||||
CONFIG_LM_PW_DEFAULT),
|
||||
config_value_read(data, "nt pass column",
|
||||
CONFIG_NT_PW_DEFAULT),
|
||||
config_value_read(data, "plain pass column",
|
||||
CONFIG_PLAIN_PW_DEFAULT),
|
||||
config_value_read(data, "acct ctrl column",
|
||||
CONFIG_ACCT_CTRL_DEFAULT),
|
||||
config_value_read(data, "unknown 3 column",
|
||||
CONFIG_UNKNOWN_3_DEFAULT),
|
||||
config_value_read(data, "logon divs column",
|
||||
CONFIG_LOGON_DIVS_DEFAULT),
|
||||
config_value_read(data, "hours len column",
|
||||
CONFIG_HOURS_LEN_DEFAULT),
|
||||
config_value_read(data, "unknown 5 column",
|
||||
CONFIG_UNKNOWN_5_DEFAULT),
|
||||
config_value_read(data, "unknown 6 column",
|
||||
CONFIG_UNKNOWN_6_DEFAULT),
|
||||
config_value(data, "table", CONFIG_TABLE_DEFAULT)
|
||||
);
|
||||
|
||||
ret = mysql_query(data->handle, query);
|
||||
SAFE_FREE(query);
|
||||
|
||||
if (ret) {
|
||||
DEBUG(0,
|
||||
("Error executing query: %s\n", mysql_error(data->handle)));
|
||||
return False;
|
||||
}
|
||||
|
||||
data->pwent = mysql_store_result(data->handle);
|
||||
|
||||
if (data->pwent == NULL) {
|
||||
DEBUG(0,
|
||||
("Error storing results: %s\n", mysql_error(data->handle)));
|
||||
return False;
|
||||
}
|
||||
|
||||
DEBUG(5,
|
||||
("mysqlsam_setsampwent succeeded(%d results)!\n",
|
||||
mysql_num_fields(data->pwent)));
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
End enumeration of the passwd list.
|
||||
****************************************************************/
|
||||
|
||||
static void
|
||||
mysqlsam_endsampwent(struct pdb_methods *methods)
|
||||
{
|
||||
struct pdb_mysql_data *data =
|
||||
(struct pdb_mysql_data *) methods->private_data;
|
||||
|
||||
if (data == NULL) {
|
||||
DEBUG(0, ("invalid handle!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->pwent != NULL)
|
||||
mysql_free_result(data->pwent);
|
||||
|
||||
data->pwent = NULL;
|
||||
|
||||
DEBUG(5, ("mysql_endsampwent called\n"));
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
Get one SAM_ACCOUNT from the list (next in line)
|
||||
*****************************************************************/
|
||||
|
||||
static BOOL
|
||||
mysqlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
|
||||
{
|
||||
struct pdb_mysql_data *data;
|
||||
|
||||
SET_DATA(data, methods);
|
||||
|
||||
if (data->pwent == NULL) {
|
||||
DEBUG(0, ("invalid pwent\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
return row_to_sam_account(data->pwent, user);
|
||||
}
|
||||
|
||||
BOOL
|
||||
mysqlsam_select_by_field(struct pdb_methods * methods, SAM_ACCOUNT * user,
|
||||
const char *field, const char *sname)
|
||||
{
|
||||
char *esc_sname;
|
||||
char *query;
|
||||
int ret;
|
||||
MYSQL_RES *res;
|
||||
struct pdb_mysql_data *data;
|
||||
|
||||
SET_DATA(data, methods);
|
||||
|
||||
esc_sname = malloc(strlen(sname) * 2 + 1);
|
||||
if (!esc_sname) {
|
||||
DEBUG(0, ("Not enough memory available!\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
DEBUG(5,
|
||||
("mysqlsam_select_by_field: getting data where %s = %s(nonescaped)\n",
|
||||
field, sname));
|
||||
|
||||
/* Escape sname */
|
||||
mysql_real_escape_string(data->handle, esc_sname, (char *) sname,
|
||||
strlen(sname));
|
||||
|
||||
if (user == NULL) {
|
||||
DEBUG(0, ("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n"));
|
||||
SAFE_FREE(esc_sname);
|
||||
return False;
|
||||
}
|
||||
|
||||
asprintf(&query,
|
||||
"SELECT %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s FROM %s WHERE %s = '%s'",
|
||||
config_value_read(data, "logon time column",
|
||||
CONFIG_LOGON_TIME_DEFAULT),
|
||||
config_value_read(data, "logoff time column",
|
||||
CONFIG_LOGOFF_TIME_DEFAULT),
|
||||
config_value_read(data, "kickoff time column",
|
||||
CONFIG_KICKOFF_TIME_DEFAULT),
|
||||
config_value_read(data, "pass last set time column",
|
||||
CONFIG_PASS_LAST_SET_TIME_DEFAULT),
|
||||
config_value_read(data, "pass can change time column",
|
||||
CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT),
|
||||
config_value_read(data, "pass must change time column",
|
||||
CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT),
|
||||
config_value_read(data, "username column",
|
||||
CONFIG_USERNAME_DEFAULT),
|
||||
config_value_read(data, "domain column",
|
||||
CONFIG_DOMAIN_DEFAULT),
|
||||
config_value_read(data, "nt username column",
|
||||
CONFIG_NT_USERNAME_DEFAULT),
|
||||
config_value_read(data, "fullname column",
|
||||
CONFIG_FULLNAME_DEFAULT),
|
||||
config_value_read(data, "home dir column",
|
||||
CONFIG_HOME_DIR_DEFAULT),
|
||||
config_value_read(data, "dir drive column",
|
||||
CONFIG_DIR_DRIVE_DEFAULT),
|
||||
config_value_read(data, "logon script column",
|
||||
CONFIG_LOGON_SCRIPT_DEFAULT),
|
||||
config_value_read(data, "profile path column",
|
||||
CONFIG_PROFILE_PATH_DEFAULT),
|
||||
config_value_read(data, "acct desc column",
|
||||
CONFIG_ACCT_DESC_DEFAULT),
|
||||
config_value_read(data, "workstations column",
|
||||
CONFIG_WORKSTATIONS_DEFAULT),
|
||||
config_value_read(data, "unknown string column",
|
||||
CONFIG_UNKNOWN_STR_DEFAULT),
|
||||
config_value_read(data, "munged dial column",
|
||||
CONFIG_MUNGED_DIAL_DEFAULT),
|
||||
config_value_read(data, "uid column", CONFIG_UID_DEFAULT),
|
||||
config_value_read(data, "gid column", CONFIG_GID_DEFAULT),
|
||||
config_value_read(data, "user sid column",
|
||||
CONFIG_USER_SID_DEFAULT),
|
||||
config_value_read(data, "group sid column",
|
||||
CONFIG_GROUP_SID_DEFAULT),
|
||||
config_value_read(data, "lanman pass column",
|
||||
CONFIG_LM_PW_DEFAULT),
|
||||
config_value_read(data, "nt pass column",
|
||||
CONFIG_NT_PW_DEFAULT),
|
||||
config_value_read(data, "plain pass column",
|
||||
CONFIG_PLAIN_PW_DEFAULT),
|
||||
config_value_read(data, "acct ctrl column",
|
||||
CONFIG_ACCT_CTRL_DEFAULT),
|
||||
config_value_read(data, "unknown 3 column",
|
||||
CONFIG_UNKNOWN_3_DEFAULT),
|
||||
config_value_read(data, "logon divs column",
|
||||
CONFIG_LOGON_DIVS_DEFAULT),
|
||||
config_value_read(data, "hours len column",
|
||||
CONFIG_HOURS_LEN_DEFAULT),
|
||||
config_value_read(data, "unknown 5 column",
|
||||
CONFIG_UNKNOWN_5_DEFAULT),
|
||||
config_value_read(data, "unknown 6 column",
|
||||
CONFIG_UNKNOWN_6_DEFAULT),
|
||||
config_value(data, "table", CONFIG_TABLE_DEFAULT), field,
|
||||
esc_sname);
|
||||
|
||||
SAFE_FREE(esc_sname);
|
||||
|
||||
ret = mysql_query(data->handle, query);
|
||||
|
||||
SAFE_FREE(query);
|
||||
|
||||
if (ret) {
|
||||
DEBUG(0,
|
||||
("Error while executing MySQL query: %s\n",
|
||||
mysql_error(data->handle)));
|
||||
return False;
|
||||
}
|
||||
|
||||
res = mysql_store_result(data->handle);
|
||||
if (res == NULL) {
|
||||
DEBUG(0,
|
||||
("Error storing results: %s\n", mysql_error(data->handle)));
|
||||
return False;
|
||||
}
|
||||
|
||||
ret = row_to_sam_account(res, user);
|
||||
mysql_free_result(res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
Lookup a name in the SAM database
|
||||
******************************************************************/
|
||||
|
||||
static BOOL
|
||||
mysqlsam_getsampwnam(struct pdb_methods *methods, SAM_ACCOUNT * user,
|
||||
const char *sname)
|
||||
{
|
||||
struct pdb_mysql_data *data;
|
||||
|
||||
SET_DATA(data, methods);
|
||||
|
||||
if (!sname) {
|
||||
DEBUG(0, ("invalid name specified"));
|
||||
return False;
|
||||
}
|
||||
return mysqlsam_select_by_field(methods, user,
|
||||
config_value_read(data, "username column",
|
||||
CONFIG_USERNAME_DEFAULT), sname);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
Search by sid
|
||||
**************************************************************************/
|
||||
|
||||
static BOOL
|
||||
mysqlsam_getsampwsid(struct pdb_methods *methods, SAM_ACCOUNT * user,
|
||||
const DOM_SID * sid)
|
||||
{
|
||||
BOOL ret = False;
|
||||
struct pdb_mysql_data *data;
|
||||
fstring sid_str;
|
||||
|
||||
SET_DATA(data, methods);
|
||||
|
||||
sid_to_string(sid_str, sid);
|
||||
|
||||
ret =
|
||||
mysqlsam_select_by_field(methods, user,
|
||||
config_value_read(data, "user sid column",
|
||||
CONFIG_USER_SID_DEFAULT), sid_str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
Delete a SAM_ACCOUNT
|
||||
****************************************************************************/
|
||||
|
||||
static BOOL
|
||||
mysqlsam_delete_sam_account(struct pdb_methods *methods,
|
||||
SAM_ACCOUNT * sam_pass)
|
||||
{
|
||||
const char *sname = pdb_get_username(sam_pass);
|
||||
char *esc;
|
||||
char *query;
|
||||
int ret;
|
||||
struct pdb_mysql_data *data;
|
||||
|
||||
SET_DATA(data, methods);
|
||||
|
||||
if (!methods) {
|
||||
DEBUG(0, ("invalid methods!\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
data = (struct pdb_mysql_data *) methods->private_data;
|
||||
if (!data || !(data->handle)) {
|
||||
DEBUG(0, ("invalid handle!\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
if (!sname) {
|
||||
DEBUG(0, ("invalid name specified\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Escape sname */
|
||||
esc = malloc(strlen(sname) * 2 + 1);
|
||||
if (!esc) {
|
||||
DEBUG(0, ("Can't allocate memory to store escaped name\n"));
|
||||
return False;
|
||||
}
|
||||
mysql_real_escape_string(data->handle, esc, (char *) sname,
|
||||
strlen(sname));
|
||||
|
||||
asprintf(&query, "DELETE FROM %s WHERE %s = '%s'",
|
||||
config_value(data, "table", CONFIG_TABLE_DEFAULT),
|
||||
config_value_read(data, "username column",
|
||||
CONFIG_USERNAME_DEFAULT), esc);
|
||||
|
||||
SAFE_FREE(esc);
|
||||
|
||||
ret = mysql_query(data->handle, query);
|
||||
|
||||
SAFE_FREE(query);
|
||||
|
||||
if (ret) {
|
||||
DEBUG(0,
|
||||
("Error while executing query: %s\n",
|
||||
mysql_error(data->handle)));
|
||||
return False;
|
||||
}
|
||||
|
||||
DEBUG(5, ("User '%s' deleted\n", sname));
|
||||
return True;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
mysqlsam_replace_sam_account(struct pdb_methods *methods,
|
||||
const SAM_ACCOUNT * newpwd, char isupdate)
|
||||
{
|
||||
pstring temp;
|
||||
uint32 store = pdb_get_init_flag(newpwd);
|
||||
struct pdb_mysql_data *data;
|
||||
pdb_mysql_query query;
|
||||
fstring sid_str;
|
||||
|
||||
if (!methods) {
|
||||
DEBUG(0, ("invalid methods!\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
data = (struct pdb_mysql_data *) methods->private_data;
|
||||
if (data == NULL || data->handle == NULL) {
|
||||
DEBUG(0, ("invalid handle!\n"));
|
||||
return False;
|
||||
}
|
||||
query.update = isupdate;
|
||||
|
||||
/* I know this is somewhat overkill but only the talloc
|
||||
* functions have asprint_append and the 'normal' asprintf
|
||||
* is a GNU extension */
|
||||
query.mem_ctx = talloc_init();
|
||||
query.part2 = talloc_asprintf(query.mem_ctx, "%s", "");
|
||||
if (query.update) {
|
||||
query.part1 =
|
||||
talloc_asprintf(query.mem_ctx, "UPDATE %s SET ",
|
||||
config_value(data, "table",
|
||||
CONFIG_TABLE_DEFAULT));
|
||||
} else {
|
||||
query.part1 =
|
||||
talloc_asprintf(query.mem_ctx, "INSERT INTO %s (",
|
||||
config_value(data, "table",
|
||||
CONFIG_TABLE_DEFAULT));
|
||||
}
|
||||
|
||||
pdb_mysql_int_field(methods, &query,
|
||||
config_value_write(data, "acct ctrl column",
|
||||
CONFIG_ACCT_CTRL_DEFAULT),
|
||||
pdb_get_acct_ctrl(newpwd));
|
||||
|
||||
if (store & FLAG_SAM_LOGONTIME) {
|
||||
pdb_mysql_int_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"logon time column",
|
||||
CONFIG_LOGON_TIME_DEFAULT),
|
||||
pdb_get_logon_time(newpwd));
|
||||
}
|
||||
|
||||
if (store & FLAG_SAM_LOGOFFTIME) {
|
||||
pdb_mysql_int_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"logoff time column",
|
||||
CONFIG_LOGOFF_TIME_DEFAULT),
|
||||
pdb_get_logoff_time(newpwd));
|
||||
}
|
||||
|
||||
if (store & FLAG_SAM_KICKOFFTIME) {
|
||||
pdb_mysql_int_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"kickoff time column",
|
||||
CONFIG_KICKOFF_TIME_DEFAULT),
|
||||
pdb_get_kickoff_time(newpwd));
|
||||
}
|
||||
|
||||
if (store & FLAG_SAM_CANCHANGETIME) {
|
||||
pdb_mysql_int_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"pass can change time column",
|
||||
CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT),
|
||||
pdb_get_pass_can_change_time(newpwd));
|
||||
}
|
||||
|
||||
if (store & FLAG_SAM_MUSTCHANGETIME) {
|
||||
pdb_mysql_int_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"pass must change time column",
|
||||
CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT),
|
||||
pdb_get_pass_must_change_time(newpwd));
|
||||
}
|
||||
|
||||
if (pdb_get_pass_last_set_time(newpwd)) {
|
||||
pdb_mysql_int_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"pass must change time column",
|
||||
CONFIG_PASS_LAST_SET_TIME_DEFAULT),
|
||||
pdb_get_pass_last_set_time(newpwd));
|
||||
}
|
||||
|
||||
if (pdb_get_hours_len(newpwd)) {
|
||||
pdb_mysql_int_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"hours len column",
|
||||
CONFIG_HOURS_LEN_DEFAULT),
|
||||
pdb_get_hours_len(newpwd));
|
||||
}
|
||||
|
||||
if (pdb_get_logon_divs(newpwd)) {
|
||||
pdb_mysql_int_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"logon divs column",
|
||||
CONFIG_LOGON_DIVS_DEFAULT),
|
||||
pdb_get_logon_divs(newpwd));
|
||||
}
|
||||
|
||||
if (store & FLAG_SAM_UID) {
|
||||
pdb_mysql_int_field(methods, &query,
|
||||
config_value_write(data, "uid column",
|
||||
CONFIG_UID_DEFAULT),
|
||||
pdb_get_uid(newpwd));
|
||||
}
|
||||
|
||||
if (store & FLAG_SAM_GID) {
|
||||
pdb_mysql_int_field(methods, &query,
|
||||
config_value_write(data, "gid column",
|
||||
CONFIG_GID_DEFAULT),
|
||||
pdb_get_gid(newpwd));
|
||||
}
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data, "user sid column",
|
||||
CONFIG_USER_SID_DEFAULT),
|
||||
sid_to_string(sid_str, (DOM_SID *)
|
||||
pdb_get_user_sid(newpwd)));
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data, "group sid column",
|
||||
CONFIG_GROUP_SID_DEFAULT),
|
||||
sid_to_string(sid_str, (DOM_SID *)
|
||||
pdb_get_group_sid(newpwd)));
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data, "username column",
|
||||
CONFIG_USERNAME_DEFAULT),
|
||||
pdb_get_username(newpwd));
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data, "domain column",
|
||||
CONFIG_DOMAIN_DEFAULT),
|
||||
pdb_get_domain(newpwd));
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"nt username column",
|
||||
CONFIG_NT_USERNAME_DEFAULT),
|
||||
pdb_get_nt_username(newpwd));
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data, "fullname column",
|
||||
CONFIG_FULLNAME_DEFAULT),
|
||||
pdb_get_fullname(newpwd));
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"logon script column",
|
||||
CONFIG_LOGON_SCRIPT_DEFAULT),
|
||||
pdb_get_logon_script(newpwd));
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"profile path column",
|
||||
CONFIG_PROFILE_PATH_DEFAULT),
|
||||
pdb_get_profile_path(newpwd));
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data, "dir drive column",
|
||||
CONFIG_DIR_DRIVE_DEFAULT),
|
||||
pdb_get_dir_drive(newpwd));
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data, "home dir column",
|
||||
CONFIG_HOME_DIR_DEFAULT),
|
||||
pdb_get_homedir(newpwd));
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"workstations column",
|
||||
CONFIG_WORKSTATIONS_DEFAULT),
|
||||
pdb_get_workstations(newpwd));
|
||||
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"unknown string column",
|
||||
CONFIG_UNKNOWN_STR_DEFAULT),
|
||||
pdb_get_workstations(newpwd));
|
||||
|
||||
pdb_sethexpwd(temp, pdb_get_lanman_passwd(newpwd),
|
||||
pdb_get_acct_ctrl(newpwd));
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data,
|
||||
"lanman pass column",
|
||||
CONFIG_LM_PW_DEFAULT), temp);
|
||||
|
||||
pdb_sethexpwd(temp, pdb_get_nt_passwd(newpwd),
|
||||
pdb_get_acct_ctrl(newpwd));
|
||||
pdb_mysql_string_field(methods, &query,
|
||||
config_value_write(data, "nt pass column",
|
||||
CONFIG_NT_PW_DEFAULT), temp);
|
||||
|
||||
if (query.update) {
|
||||
query.part1[strlen(query.part1) - 1] = '\0';
|
||||
query.part1 =
|
||||
talloc_asprintf_append(query.mem_ctx, query.part1,
|
||||
" WHERE %s = '%s'",
|
||||
config_value_read(data,
|
||||
"user sid column",
|
||||
CONFIG_USER_SID_DEFAULT),
|
||||
sid_to_string(sid_str, (DOM_SID *)
|
||||
pdb_get_user_sid
|
||||
(newpwd)));
|
||||
} else {
|
||||
query.part2[strlen(query.part2) - 1] = ')';
|
||||
query.part1[strlen(query.part1) - 1] = ')';
|
||||
query.part1 =
|
||||
talloc_asprintf_append(query.mem_ctx, query.part1,
|
||||
" VALUES (%s", query.part2);
|
||||
}
|
||||
|
||||
DEBUG(0, ("%s\n", query.part1));
|
||||
/* Execute the query */
|
||||
if (mysql_query(data->handle, query.part1)) {
|
||||
DEBUG(0,
|
||||
("Error executing %s, %s\n", query.part1,
|
||||
mysql_error(data->handle)));
|
||||
return False;
|
||||
}
|
||||
talloc_destroy(query.mem_ctx);
|
||||
return True;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
mysqlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * newpwd)
|
||||
{
|
||||
return mysqlsam_replace_sam_account(methods, newpwd, 0);
|
||||
}
|
||||
|
||||
static BOOL
|
||||
mysqlsam_update_sam_account(struct pdb_methods *methods,
|
||||
SAM_ACCOUNT * newpwd)
|
||||
{
|
||||
return mysqlsam_replace_sam_account(methods, newpwd, 1);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
pdb_init(PDB_CONTEXT * pdb_context, PDB_METHODS ** pdb_method,
|
||||
char *location)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct pdb_mysql_data *data;
|
||||
|
||||
mysqlsam_debug_level = debug_add_class("mysqlsam");
|
||||
if (mysqlsam_debug_level == -1) {
|
||||
mysqlsam_debug_level = DBGC_ALL;
|
||||
DEBUG(0,
|
||||
("mysqlsam: Couldn't register custom debugging class!\n"));
|
||||
}
|
||||
|
||||
if (!pdb_context) {
|
||||
DEBUG(0, ("invalid pdb_methods specified\n"));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK
|
||||
(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
(*pdb_method)->name = "mysqlsam";
|
||||
|
||||
(*pdb_method)->setsampwent = mysqlsam_setsampwent;
|
||||
(*pdb_method)->endsampwent = mysqlsam_endsampwent;
|
||||
(*pdb_method)->getsampwent = mysqlsam_getsampwent;
|
||||
(*pdb_method)->getsampwnam = mysqlsam_getsampwnam;
|
||||
(*pdb_method)->getsampwsid = mysqlsam_getsampwsid;
|
||||
(*pdb_method)->add_sam_account = mysqlsam_add_sam_account;
|
||||
(*pdb_method)->update_sam_account = mysqlsam_update_sam_account;
|
||||
(*pdb_method)->delete_sam_account = mysqlsam_delete_sam_account;
|
||||
|
||||
data = talloc(pdb_context->mem_ctx, sizeof(struct pdb_mysql_data));
|
||||
(*pdb_method)->private_data = data;
|
||||
data->handle = NULL;
|
||||
data->pwent = NULL;
|
||||
|
||||
if (!location) {
|
||||
DEBUG(0, ("No identifier specified. See README for details\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
data->location = smb_xstrdup(location);
|
||||
|
||||
DEBUG(1,
|
||||
("Connecting to database server, host: %s, user: %s, password: %s, database: %s, port: %d\n",
|
||||
config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
|
||||
config_value(data, "mysql user", CONFIG_USER_DEFAULT),
|
||||
config_value(data, "mysql password", CONFIG_PASS_DEFAULT),
|
||||
config_value(data, "mysql database", CONFIG_DB_DEFAULT),
|
||||
xatol(config_value(data, "mysql port", CONFIG_PORT_DEFAULT))));
|
||||
|
||||
/* Do the mysql initialization */
|
||||
data->handle = mysql_init(NULL);
|
||||
if (!data->handle) {
|
||||
DEBUG(0, ("Failed to connect to server\n"));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
/* Process correct entry in $HOME/.my.conf */
|
||||
if (!mysql_real_connect(data->handle,
|
||||
config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
|
||||
config_value(data, "mysql user", CONFIG_USER_DEFAULT),
|
||||
config_value(data, "mysql password", CONFIG_PASS_DEFAULT),
|
||||
config_value(data, "mysql database", CONFIG_DB_DEFAULT),
|
||||
xatol(config_value (data, "mysql port", CONFIG_PORT_DEFAULT)),
|
||||
NULL, 0)) {
|
||||
DEBUG(0,
|
||||
("Failed to connect to mysql database: error: %s\n",
|
||||
mysql_error(data->handle)));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
DEBUG(5, ("Connected to mysql db\n"));
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
13
examples/pdb/xml/ChangeLog
Normal file
13
examples/pdb/xml/ChangeLog
Normal file
@ -0,0 +1,13 @@
|
||||
** This file is now deprecated - use CVS' log features **
|
||||
|
||||
2002-06-13 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Use SID's instead of RID's (just like samba-HEAD CVS)
|
||||
* Released 1.1
|
||||
|
||||
2002-05-26 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Update read support (didn't support all elements yet)
|
||||
* Released 1.0
|
||||
|
||||
2002-05-26 Jelmer Vernooij <jelmer@samba.org>
|
||||
* Initial release
|
||||
* Released 0.5
|
33
examples/pdb/xml/Makefile.in
Normal file
33
examples/pdb/xml/Makefile.in
Normal file
@ -0,0 +1,33 @@
|
||||
PDB_OBJS = pdb_xml.so
|
||||
PDB_CFLAGS = `xml2-config --cflags`
|
||||
PDB_LDFLAGS = `xml2-config --libs`
|
||||
|
||||
include $(MAKEFILE)
|
||||
|
||||
CC = @CC@
|
||||
LIBTOOL = libtool
|
||||
CFLAGS = @CFLAGS@ $(PDB_CFLAGS)
|
||||
CPPFLAGS = @CPPFLAGS@ $(PDB_CPPFLAGS)
|
||||
LDFLAGS = @LDFLAGS@ $(PDB_LDFLAGS)
|
||||
LDSHFLAGS = -shared
|
||||
srcdir = @builddir@
|
||||
FLAGS = $(CFLAGS) -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I$(srcdir)/smbwrapper -I. $(CPPFLAGS) -I$(srcdir)
|
||||
|
||||
# Default target
|
||||
|
||||
default: $(PDB_OBJS)
|
||||
|
||||
# Pattern rules
|
||||
|
||||
%.so: %.lo
|
||||
$(LIBTOOL) $(CC) $(LDSHFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
%.lo: %.c
|
||||
$(LIBTOOL) $(CC) $(FLAGS) -c $<
|
||||
|
||||
# Misc targets
|
||||
|
||||
clean:
|
||||
rm -rf .libs
|
||||
rm -f core *~ *% *.bak \
|
||||
$(PDB_OBJS) $(PDB_OBJS:.so=.o) $(PDB_OBJS:.so=.lo)
|
14
examples/pdb/xml/README
Normal file
14
examples/pdb/xml/README
Normal file
@ -0,0 +1,14 @@
|
||||
Readme for samba pdb xml 0.5
|
||||
--
|
||||
This module requires libxml2 to be installed.
|
||||
|
||||
The usage of pdb_xml is pretty straightforward. To export data, use:
|
||||
|
||||
pdbedit -e plugin:/usr/lib/samba/pdb_xml.so:filename
|
||||
|
||||
(where filename is the name of the file to put the data in)
|
||||
To import data, use:
|
||||
|
||||
pdbedit -i plugin:/usr/lib/samba/pdb_xml.so:filename -e <current-pdb>
|
||||
|
||||
Where filename is the name to read the data from and <current-pdb> to put it in.
|
6
examples/pdb/xml/TODO
Normal file
6
examples/pdb/xml/TODO
Normal file
@ -0,0 +1,6 @@
|
||||
- Be faster. Don't rewrite the whole file when adding a user, but store
|
||||
it in the memory and save it when exiting. Requires changes to samba source.
|
||||
Gives the ability to read/write to standard input/output
|
||||
- Do locking!
|
||||
- Better names!
|
||||
- Support stdin ?
|
561
examples/pdb/xml/pdb_xml.c
Normal file
561
examples/pdb/xml/pdb_xml.c
Normal file
@ -0,0 +1,561 @@
|
||||
|
||||
/*
|
||||
* XML password backend for samba
|
||||
* Copyright (C) Jelmer Vernooij 2002
|
||||
* Some parts based on the libxml gjobread example by Daniel Veillard
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 675
|
||||
* Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* FIXME: Support stdin input by using '-' */
|
||||
|
||||
#define XML_URL "http://www.samba.org/ns"
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include <libxml/xmlmemory.h>
|
||||
#include <libxml/parser.h>
|
||||
|
||||
static int xmlsam_debug_level = DBGC_ALL;
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS xmlsam_debug_level
|
||||
|
||||
PDB_MODULE_VERSIONING_MAGIC
|
||||
|
||||
static char * iota(int a) {
|
||||
static char tmp[10];
|
||||
|
||||
snprintf(tmp, 9, "%d", a);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
BOOL
|
||||
parsePass(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
|
||||
{
|
||||
pstring temp;
|
||||
|
||||
cur = cur->xmlChildrenNode;
|
||||
while (cur != NULL) {
|
||||
if (strcmp(cur->name, "crypt"))
|
||||
DEBUG(0, ("Unknown element %s\n", cur->name));
|
||||
else {
|
||||
if (!strcmp(xmlGetProp(cur, "type"), "nt")
|
||||
&&
|
||||
pdb_gethexpwd(xmlNodeListGetString
|
||||
(doc, cur->xmlChildrenNode, 1), temp))
|
||||
pdb_set_nt_passwd(u, temp);
|
||||
else if (!strcmp(xmlGetProp(cur, "type"), "lanman")
|
||||
&&
|
||||
pdb_gethexpwd(xmlNodeListGetString
|
||||
(doc, cur->xmlChildrenNode, 1), temp))
|
||||
pdb_set_lanman_passwd(u, temp);
|
||||
else
|
||||
DEBUG(0,
|
||||
("Unknown crypt type: %s\n",
|
||||
xmlGetProp(cur, "type")));
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
BOOL
|
||||
parseUser(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
|
||||
{
|
||||
char *tmp;
|
||||
DOM_SID sid;
|
||||
|
||||
tmp = xmlGetProp(cur, "sid");
|
||||
if (tmp){
|
||||
string_to_sid(&sid, tmp);
|
||||
pdb_set_user_sid(u, &sid);
|
||||
}
|
||||
tmp = xmlGetProp(cur, "uid");
|
||||
if (tmp)
|
||||
pdb_set_uid(u, atol(tmp));
|
||||
pdb_set_username(u, xmlGetProp(cur, "name"));
|
||||
/* We don't care what the top level element name is */
|
||||
cur = cur->xmlChildrenNode;
|
||||
while (cur != NULL) {
|
||||
if ((!strcmp(cur->name, "group")) && (cur->ns == ns)) {
|
||||
tmp = xmlGetProp(cur, "gid");
|
||||
if (tmp)
|
||||
pdb_set_gid(u, atol(tmp));
|
||||
tmp = xmlGetProp(cur, "sid");
|
||||
if (tmp){
|
||||
string_to_sid(&sid, tmp);
|
||||
pdb_set_group_sid(u, &sid);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((!strcmp(cur->name, "domain")) && (cur->ns == ns))
|
||||
pdb_set_domain(u,
|
||||
xmlNodeListGetString(doc, cur->xmlChildrenNode,
|
||||
1));
|
||||
|
||||
else if (!strcmp(cur->name, "fullname") && cur->ns == ns)
|
||||
pdb_set_fullname(u,
|
||||
xmlNodeListGetString(doc,
|
||||
cur->xmlChildrenNode,
|
||||
1));
|
||||
|
||||
else if (!strcmp(cur->name, "nt_username") && cur->ns == ns)
|
||||
pdb_set_nt_username(u,
|
||||
xmlNodeListGetString(doc,
|
||||
cur->xmlChildrenNode,
|
||||
1));
|
||||
|
||||
else if (!strcmp(cur->name, "logon_script") && cur->ns == ns)
|
||||
pdb_set_logon_script(u,
|
||||
xmlNodeListGetString(doc,
|
||||
cur->xmlChildrenNode,
|
||||
1), True);
|
||||
|
||||
else if (!strcmp(cur->name, "profile_path") && cur->ns == ns)
|
||||
pdb_set_profile_path(u,
|
||||
xmlNodeListGetString(doc,
|
||||
cur->xmlChildrenNode,
|
||||
1), True);
|
||||
|
||||
else if (!strcmp(cur->name, "logon_time") && cur->ns == ns)
|
||||
pdb_set_logon_time(u,
|
||||
atol(xmlNodeListGetString
|
||||
(doc, cur->xmlChildrenNode, 1)), True);
|
||||
|
||||
else if (!strcmp(cur->name, "logoff_time") && cur->ns == ns)
|
||||
pdb_set_logoff_time(u,
|
||||
atol(xmlNodeListGetString
|
||||
(doc, cur->xmlChildrenNode, 1)),
|
||||
True);
|
||||
|
||||
else if (!strcmp(cur->name, "kickoff_time") && cur->ns == ns)
|
||||
pdb_set_kickoff_time(u,
|
||||
atol(xmlNodeListGetString
|
||||
(doc, cur->xmlChildrenNode, 1)),
|
||||
True);
|
||||
|
||||
else if (!strcmp(cur->name, "logon_divs") && cur->ns == ns)
|
||||
pdb_set_logon_divs(u,
|
||||
atol(xmlNodeListGetString
|
||||
(doc, cur->xmlChildrenNode, 1)));
|
||||
|
||||
else if (!strcmp(cur->name, "hours_len") && cur->ns == ns)
|
||||
pdb_set_hours_len(u,
|
||||
atol(xmlNodeListGetString
|
||||
(doc, cur->xmlChildrenNode, 1)));
|
||||
|
||||
else if (!strcmp(cur->name, "unknown_3") && cur->ns == ns)
|
||||
pdb_set_unknown_3(u,
|
||||
atol(xmlNodeListGetString
|
||||
(doc, cur->xmlChildrenNode, 1)));
|
||||
|
||||
else if (!strcmp(cur->name, "unknown_5") && cur->ns == ns)
|
||||
pdb_set_unknown_5(u,
|
||||
atol(xmlNodeListGetString
|
||||
(doc, cur->xmlChildrenNode, 1)));
|
||||
|
||||
else if (!strcmp(cur->name, "unknown_6") && cur->ns == ns)
|
||||
pdb_set_unknown_6(u,
|
||||
atol(xmlNodeListGetString
|
||||
(doc, cur->xmlChildrenNode, 1)));
|
||||
|
||||
else if (!strcmp(cur->name, "homedir") && cur->ns == ns)
|
||||
pdb_set_homedir(u,
|
||||
xmlNodeListGetString(doc, cur->xmlChildrenNode,
|
||||
1), True);
|
||||
|
||||
else if (!strcmp(cur->name, "unknown_str") && cur->ns == ns)
|
||||
pdb_set_unknown_str(u,
|
||||
xmlNodeListGetString(doc,
|
||||
cur->xmlChildrenNode,
|
||||
1));
|
||||
|
||||
else if (!strcmp(cur->name, "dir_drive") && cur->ns == ns)
|
||||
pdb_set_dir_drive(u,
|
||||
xmlNodeListGetString(doc,
|
||||
cur->xmlChildrenNode,
|
||||
1), True);
|
||||
|
||||
else if (!strcmp(cur->name, "munged_dial") && cur->ns == ns)
|
||||
pdb_set_munged_dial(u,
|
||||
xmlNodeListGetString(doc,
|
||||
cur->xmlChildrenNode,
|
||||
1));
|
||||
|
||||
else if (!strcmp(cur->name, "acct_desc") && cur->ns == ns)
|
||||
pdb_set_acct_desc(u,
|
||||
xmlNodeListGetString(doc,
|
||||
cur->xmlChildrenNode,
|
||||
1));
|
||||
|
||||
else if (!strcmp(cur->name, "acct_ctrl") && cur->ns == ns)
|
||||
pdb_set_acct_ctrl(u,
|
||||
atol(xmlNodeListGetString
|
||||
(doc, cur->xmlChildrenNode, 1)));
|
||||
|
||||
else if (!strcmp(cur->name, "workstations") && cur->ns == ns)
|
||||
pdb_set_workstations(u,
|
||||
xmlNodeListGetString(doc,
|
||||
cur->xmlChildrenNode,
|
||||
1));
|
||||
|
||||
else if ((!strcmp(cur->name, "password")) && (cur->ns == ns)) {
|
||||
tmp = xmlGetProp(cur, "last_set");
|
||||
if (tmp)
|
||||
pdb_set_pass_last_set_time(u, atol(tmp));
|
||||
tmp = xmlGetProp(cur, "must_change");
|
||||
if (tmp)
|
||||
pdb_set_pass_must_change_time(u, atol(tmp), True);
|
||||
tmp = xmlGetProp(cur, "can_change");
|
||||
if (tmp)
|
||||
pdb_set_pass_can_change_time(u, atol(tmp), True);
|
||||
parsePass(doc, ns, cur, u);
|
||||
}
|
||||
|
||||
else
|
||||
DEBUG(0, ("Unknown element %s\n", cur->name));
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
typedef struct pdb_xml {
|
||||
char *location;
|
||||
char written;
|
||||
xmlDocPtr doc;
|
||||
xmlNodePtr users;
|
||||
xmlNodePtr pwent;
|
||||
xmlNsPtr ns;
|
||||
} pdb_xml;
|
||||
|
||||
xmlNodePtr
|
||||
parseSambaXMLFile(struct pdb_xml *data)
|
||||
{
|
||||
xmlNodePtr cur;
|
||||
|
||||
data->doc = xmlParseFile(data->location);
|
||||
if (data->doc == NULL)
|
||||
return NULL;
|
||||
|
||||
cur = xmlDocGetRootElement(data->doc);
|
||||
if (!cur) {
|
||||
DEBUG(0, ("empty document\n"));
|
||||
xmlFreeDoc(data->doc);
|
||||
return NULL;
|
||||
}
|
||||
data->ns = xmlSearchNsByHref(data->doc, cur, XML_URL);
|
||||
if (!data->ns) {
|
||||
DEBUG(0,
|
||||
("document of the wrong type, samba user namespace not found\n"));
|
||||
xmlFreeDoc(data->doc);
|
||||
return NULL;
|
||||
}
|
||||
if (strcmp(cur->name, "samba")) {
|
||||
DEBUG(0, ("document of the wrong type, root node != samba"));
|
||||
xmlFreeDoc(data->doc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cur = cur->xmlChildrenNode;
|
||||
while (cur && xmlIsBlankNode(cur)) {
|
||||
cur = cur->next;
|
||||
}
|
||||
if (!cur)
|
||||
return NULL;
|
||||
if ((strcmp(cur->name, "users")) || (cur->ns != data->ns)) {
|
||||
DEBUG(0, ("document of the wrong type, was '%s', users expected",
|
||||
cur->name));
|
||||
DEBUG(0, ("xmlDocDump follows\n"));
|
||||
xmlDocDump(stderr, data->doc);
|
||||
DEBUG(0, ("xmlDocDump finished\n"));
|
||||
xmlFreeDoc(data->doc);
|
||||
return NULL;
|
||||
}
|
||||
data->users = cur;
|
||||
cur = cur->xmlChildrenNode;
|
||||
return cur;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
xmlsam_setsampwent(struct pdb_methods *methods, BOOL update)
|
||||
{
|
||||
pdb_xml *data;
|
||||
|
||||
if (!methods) {
|
||||
DEBUG(0, ("Invalid methods\n"));
|
||||
return False;
|
||||
}
|
||||
data = (pdb_xml *) methods->private_data;
|
||||
if (!data) {
|
||||
DEBUG(0, ("Invalid pdb_xml_data\n"));
|
||||
return False;
|
||||
}
|
||||
data->pwent = parseSambaXMLFile(data);
|
||||
if (!data->pwent)
|
||||
return False;
|
||||
return True;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
End enumeration of the passwd list.
|
||||
****************************************************************/
|
||||
|
||||
static void
|
||||
xmlsam_endsampwent(struct pdb_methods *methods)
|
||||
{
|
||||
pdb_xml *data;
|
||||
|
||||
if (!methods) {
|
||||
DEBUG(0, ("Invalid methods\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
data = (pdb_xml *) methods->private_data;
|
||||
|
||||
if (!data) {
|
||||
DEBUG(0, ("Invalid pdb_xml_data\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
xmlFreeDoc(data->doc);
|
||||
data->doc = NULL;
|
||||
data->pwent = NULL;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
Get one SAM_ACCOUNT from the list (next in line)
|
||||
*****************************************************************/
|
||||
|
||||
static BOOL
|
||||
xmlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
|
||||
{
|
||||
pdb_xml *data;
|
||||
|
||||
if (!methods) {
|
||||
DEBUG(0, ("Invalid methods\n"));
|
||||
return False;
|
||||
}
|
||||
data = (pdb_xml *) methods->private_data;
|
||||
|
||||
if (!data) {
|
||||
DEBUG(0, ("Invalid pdb_xml_data\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
while (data->pwent) {
|
||||
if ((!strcmp(data->pwent->name, "user")) &&
|
||||
(data->pwent->ns == data->ns)) {
|
||||
|
||||
parseUser(data->doc, data->ns, data->pwent, user);
|
||||
data->pwent = data->pwent->next;
|
||||
return True;
|
||||
}
|
||||
data->pwent = data->pwent->next;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
Adds an existing SAM_ACCOUNT
|
||||
****************************************************************************/
|
||||
|
||||
static BOOL
|
||||
xmlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * u)
|
||||
{
|
||||
pstring temp;
|
||||
fstring sid_str;
|
||||
xmlNodePtr cur, user, pass, root;
|
||||
pdb_xml *data;
|
||||
uint32 store = pdb_get_init_flag(u);
|
||||
|
||||
DEBUG(10, ("xmlsam_add_sam_account called!\n"));
|
||||
|
||||
if (!methods) {
|
||||
DEBUG(0, ("Invalid methods\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
data = (pdb_xml *) methods->private_data;
|
||||
if (!data) {
|
||||
DEBUG(0, ("Invalid pdb_xml_data\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Create a new document if we can't open the current one */
|
||||
if (!parseSambaXMLFile(data)) {
|
||||
DEBUG(0, ("Can't load current XML file, creating a new one\n"));
|
||||
data->doc = xmlNewDoc(XML_DEFAULT_VERSION);
|
||||
root = xmlNewDocNode(data->doc, NULL, "samba", NULL);
|
||||
cur = xmlDocSetRootElement(data->doc, root);
|
||||
data->ns = xmlNewNs(root, XML_URL, "samba");
|
||||
data->users = xmlNewChild(root, data->ns, "users", NULL);
|
||||
}
|
||||
|
||||
user = xmlNewChild(data->users, data->ns, "user", NULL);
|
||||
xmlNewProp(user, "sid",
|
||||
sid_to_string(sid_str, pdb_get_user_sid(u)));
|
||||
if (store & FLAG_SAM_UID)
|
||||
xmlNewProp(user, "uid", iota(pdb_get_uid(u)));
|
||||
|
||||
if (pdb_get_username(u) && strcmp(pdb_get_username(u), ""))
|
||||
xmlNewProp(user, "name", pdb_get_username(u));
|
||||
|
||||
cur = xmlNewChild(user, data->ns, "group", NULL);
|
||||
|
||||
xmlNewProp(cur, "sid",
|
||||
sid_to_string(sid_str, pdb_get_group_sid(u)));
|
||||
if (store & FLAG_SAM_GID)
|
||||
xmlNewProp(cur, "gid", iota(pdb_get_gid(u)));
|
||||
|
||||
if (store & FLAG_SAM_LOGONTIME)
|
||||
xmlNewChild(user, data->ns, "login_time",
|
||||
iota(pdb_get_logon_time(u)));
|
||||
|
||||
if (store & FLAG_SAM_LOGOFFTIME)
|
||||
xmlNewChild(user, data->ns, "logoff_time",
|
||||
iota(pdb_get_logoff_time(u)));
|
||||
|
||||
if (store & FLAG_SAM_KICKOFFTIME)
|
||||
xmlNewChild(user, data->ns, "kickoff_time",
|
||||
iota(pdb_get_kickoff_time(u)));
|
||||
|
||||
if (pdb_get_domain(u) && strcmp(pdb_get_domain(u), ""))
|
||||
xmlNewChild(user, data->ns, "domain", pdb_get_domain(u));
|
||||
|
||||
if (pdb_get_nt_username(u) && strcmp(pdb_get_nt_username(u), ""))
|
||||
xmlNewChild(user, data->ns, "nt_username", pdb_get_nt_username(u));
|
||||
|
||||
if (pdb_get_fullname(u) && strcmp(pdb_get_fullname(u), ""))
|
||||
xmlNewChild(user, data->ns, "fullname", pdb_get_fullname(u));
|
||||
|
||||
if (pdb_get_homedir(u) && strcmp(pdb_get_homedir(u), ""))
|
||||
xmlNewChild(user, data->ns, "homedir", pdb_get_homedir(u));
|
||||
|
||||
if (pdb_get_dir_drive(u) && strcmp(pdb_get_dir_drive(u), ""))
|
||||
xmlNewChild(user, data->ns, "dir_drive", pdb_get_dir_drive(u));
|
||||
|
||||
if (pdb_get_logon_script(u) && strcmp(pdb_get_logon_script(u), ""))
|
||||
xmlNewChild(user, data->ns, "logon_script",
|
||||
pdb_get_logon_script(u));
|
||||
|
||||
if (pdb_get_profile_path(u) && strcmp(pdb_get_profile_path(u), ""))
|
||||
xmlNewChild(user, data->ns, "profile_path",
|
||||
pdb_get_profile_path(u));
|
||||
|
||||
if (pdb_get_acct_desc(u) && strcmp(pdb_get_acct_desc(u), ""))
|
||||
xmlNewChild(user, data->ns, "acct_desc", pdb_get_acct_desc(u));
|
||||
|
||||
if (pdb_get_workstations(u) && strcmp(pdb_get_workstations(u), ""))
|
||||
xmlNewChild(user, data->ns, "workstations",
|
||||
pdb_get_workstations(u));
|
||||
|
||||
if (pdb_get_unknown_str(u) && strcmp(pdb_get_unknown_str(u), ""))
|
||||
xmlNewChild(user, data->ns, "unknown_str", pdb_get_unknown_str(u));
|
||||
|
||||
if (pdb_get_munged_dial(u) && strcmp(pdb_get_munged_dial(u), ""))
|
||||
xmlNewChild(user, data->ns, "munged_dial", pdb_get_munged_dial(u));
|
||||
|
||||
|
||||
/* Password stuff */
|
||||
pass = xmlNewChild(user, data->ns, "password", NULL);
|
||||
if (pdb_get_pass_last_set_time(u))
|
||||
xmlNewProp(pass, "last_set", iota(pdb_get_pass_last_set_time(u)));
|
||||
if (store & FLAG_SAM_CANCHANGETIME)
|
||||
xmlNewProp(pass, "can_change",
|
||||
iota(pdb_get_pass_can_change_time(u)));
|
||||
|
||||
if (store & FLAG_SAM_MUSTCHANGETIME)
|
||||
xmlNewProp(pass, "must_change",
|
||||
iota(pdb_get_pass_must_change_time(u)));
|
||||
|
||||
|
||||
if (pdb_get_lanman_passwd(u)) {
|
||||
pdb_sethexpwd(temp, pdb_get_lanman_passwd(u),
|
||||
pdb_get_acct_ctrl(u));
|
||||
cur = xmlNewChild(pass, data->ns, "crypt", temp);
|
||||
xmlNewProp(cur, "type", "lanman");
|
||||
}
|
||||
|
||||
if (pdb_get_nt_passwd(u)) {
|
||||
pdb_sethexpwd(temp, pdb_get_nt_passwd(u), pdb_get_acct_ctrl(u));
|
||||
cur = xmlNewChild(pass, data->ns, "crypt", temp);
|
||||
xmlNewProp(cur, "type", "nt");
|
||||
}
|
||||
|
||||
xmlNewChild(user, data->ns, "acct_ctrl", iota(pdb_get_acct_ctrl(u)));
|
||||
xmlNewChild(user, data->ns, "unknown_3", iota(pdb_get_unknown3(u)));
|
||||
|
||||
if (pdb_get_logon_divs(u))
|
||||
xmlNewChild(user, data->ns, "logon_divs",
|
||||
iota(pdb_get_logon_divs(u)));
|
||||
|
||||
if (pdb_get_hours_len(u))
|
||||
xmlNewChild(user, data->ns, "hours_len",
|
||||
iota(pdb_get_hours_len(u)));
|
||||
|
||||
xmlNewChild(user, data->ns, "unknown_5", iota(pdb_get_unknown5(u)));
|
||||
xmlNewChild(user, data->ns, "unknown_6", iota(pdb_get_unknown6(u)));
|
||||
xmlSaveFile(data->location, data->doc);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
pdb_init(PDB_CONTEXT * pdb_context, PDB_METHODS ** pdb_method,
|
||||
const char *location)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
pdb_xml *data;
|
||||
|
||||
xmlsam_debug_level = debug_add_class("xmlsam");
|
||||
if (xmlsam_debug_level == -1) {
|
||||
xmlsam_debug_level = DBGC_ALL;
|
||||
DEBUG(0, ("xmlsam: Couldn't register custom debugging class!\n"));
|
||||
}
|
||||
|
||||
if (!pdb_context) {
|
||||
DEBUG(0, ("invalid pdb_methods specified\n"));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK
|
||||
(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
(*pdb_method)->name = "xmlsam";
|
||||
|
||||
(*pdb_method)->setsampwent = xmlsam_setsampwent;
|
||||
(*pdb_method)->endsampwent = xmlsam_endsampwent;
|
||||
(*pdb_method)->getsampwent = xmlsam_getsampwent;
|
||||
(*pdb_method)->add_sam_account = xmlsam_add_sam_account;
|
||||
(*pdb_method)->getsampwnam = NULL;
|
||||
(*pdb_method)->getsampwsid = NULL;
|
||||
(*pdb_method)->update_sam_account = NULL;
|
||||
(*pdb_method)->delete_sam_account = NULL;
|
||||
|
||||
data = talloc(pdb_context->mem_ctx, sizeof(pdb_xml));
|
||||
data->location =
|
||||
(location ? talloc_strdup(pdb_context->mem_ctx, location) : "-");
|
||||
data->pwent = NULL;
|
||||
data->written = 0;
|
||||
(*pdb_method)->private_data = data;
|
||||
|
||||
LIBXML_TEST_VERSION xmlKeepBlanksDefault(0);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
38
source3/include/adt_tree.h
Normal file
38
source3/include/adt_tree.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* Generic Abstract Data Types
|
||||
* Copyright (C) Gerald Carter 2002.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef ADT_TREE_H
|
||||
#define ADT_TREE_H
|
||||
|
||||
typedef struct _tree_node {
|
||||
struct _tree_node *parent;
|
||||
struct _tree_node **children;
|
||||
int num_children;
|
||||
char *key;
|
||||
void *data_p;
|
||||
} TREE_NODE;
|
||||
|
||||
typedef struct _tree_root {
|
||||
TREE_NODE *root;
|
||||
int (*compare)(void* x, void *y);
|
||||
void (*free)(void *p);
|
||||
} SORTED_TREE;
|
||||
|
||||
#endif
|
26
source3/include/ntioctl.h
Normal file
26
source3/include/ntioctl.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
NT ioctl code constants
|
||||
Copyright (C) Andrew Tridgell 2002
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
I'm guessing we will need to support a bunch of these eventually. For now
|
||||
we only need the sparse flag
|
||||
*/
|
||||
|
||||
#define NTIOCTL_SET_SPARSE 0x900c4
|
464
source3/lib/adt_tree.c
Normal file
464
source3/lib/adt_tree.c
Normal file
@ -0,0 +1,464 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* Generic Abstract Data Types
|
||||
* Copyright (C) Gerald Carter 2002.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
Initialize the tree's root. The cmp_fn is a callback function used
|
||||
for comparision of two children
|
||||
*************************************************************************/
|
||||
|
||||
static BOOL trim_tree_keypath( char *path, char **base, char **new_path )
|
||||
{
|
||||
char *p;
|
||||
|
||||
*new_path = *base = NULL;
|
||||
|
||||
if ( !path )
|
||||
return False;
|
||||
|
||||
*base = path;
|
||||
|
||||
p = strchr( path, '/' );
|
||||
|
||||
if ( p ) {
|
||||
*p = '\0';
|
||||
*new_path = p+1;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
Initialize the tree's root. The cmp_fn is a callback function used
|
||||
for comparision of two children
|
||||
*************************************************************************/
|
||||
|
||||
SORTED_TREE* sorted_tree_init( void *data_p,
|
||||
int (cmp_fn)(void*, void*),
|
||||
void (free_fn)(void*) )
|
||||
{
|
||||
SORTED_TREE *tree = NULL;
|
||||
|
||||
if ( !(tree = (SORTED_TREE*)malloc( sizeof(SORTED_TREE) )) )
|
||||
return NULL;
|
||||
|
||||
ZERO_STRUCTP( tree );
|
||||
|
||||
tree->compare = cmp_fn;
|
||||
tree->free = free_fn;
|
||||
|
||||
if ( !(tree->root = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) ) {
|
||||
SAFE_FREE( tree );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ZERO_STRUCTP( tree->root );
|
||||
tree->root->data_p = data_p;
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
Delete a tree and free all allocated memory
|
||||
*************************************************************************/
|
||||
|
||||
static void sorted_tree_destroy_children( TREE_NODE *root )
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( !root )
|
||||
return;
|
||||
|
||||
for ( i=0; i<root->num_children; i++ )
|
||||
{
|
||||
sorted_tree_destroy_children( root->children[i] );
|
||||
}
|
||||
|
||||
SAFE_FREE( root->children );
|
||||
SAFE_FREE( root->key );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
Delete a tree and free all allocated memory
|
||||
*************************************************************************/
|
||||
|
||||
void sorted_tree_destroy( SORTED_TREE *tree )
|
||||
{
|
||||
if ( tree->root )
|
||||
sorted_tree_destroy_children( tree->root );
|
||||
|
||||
if ( tree->free )
|
||||
tree->free( tree->root );
|
||||
|
||||
SAFE_FREE( tree );
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
Find the next child given a key string
|
||||
*************************************************************************/
|
||||
|
||||
static TREE_NODE* sorted_tree_birth_child( TREE_NODE *node, char* key )
|
||||
{
|
||||
TREE_NODE *infant = NULL;
|
||||
TREE_NODE **siblings;
|
||||
int i;
|
||||
|
||||
if ( !(infant = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) )
|
||||
return NULL;
|
||||
|
||||
ZERO_STRUCTP( infant );
|
||||
|
||||
infant->key = strdup( key );
|
||||
infant->parent = node;
|
||||
|
||||
siblings = Realloc( node->children, sizeof(TREE_NODE*)*(node->num_children+1) );
|
||||
|
||||
if ( siblings )
|
||||
node->children = siblings;
|
||||
|
||||
node->num_children++;
|
||||
|
||||
/* first child */
|
||||
|
||||
if ( node->num_children == 1 ) {
|
||||
DEBUG(11,("sorted_tree_birth_child: First child of node [%s]! [%s]\n",
|
||||
node->key ? node->key : "NULL", infant->key ));
|
||||
node->children[0] = infant;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* multiple siblings .... (at least 2 children)
|
||||
*
|
||||
* work from the end of the list forward
|
||||
* The last child is not set at this point
|
||||
* Insert the new infanct in ascending order
|
||||
* from left to right
|
||||
*/
|
||||
|
||||
for ( i = node->num_children-1; i>=1; i-- )
|
||||
{
|
||||
DEBUG(11,("sorted_tree_birth_child: Looking for crib; infant -> [%s], child -> [%s]\n",
|
||||
infant->key, node->children[i-1]->key));
|
||||
|
||||
/* the strings should never match assuming that we
|
||||
have called sorted_tree_find_child() first */
|
||||
|
||||
if ( StrCaseCmp( infant->key, node->children[i-1]->key ) > 0 ) {
|
||||
DEBUG(11,("sorted_tree_birth_child: storing infant in i == [%d]\n",
|
||||
i));
|
||||
node->children[i] = infant;
|
||||
break;
|
||||
}
|
||||
|
||||
/* bump everything towards the end on slot */
|
||||
|
||||
node->children[i] = node->children[i-1];
|
||||
}
|
||||
|
||||
DEBUG(11,("sorted_tree_birth_child: Exiting loop (i == [%d])\n", i ));
|
||||
|
||||
/* if we haven't found the correct slot yet, the child
|
||||
will be first in the list */
|
||||
|
||||
if ( i == 0 )
|
||||
node->children[0] = infant;
|
||||
}
|
||||
|
||||
return infant;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
Find the next child given a key string
|
||||
*************************************************************************/
|
||||
|
||||
static TREE_NODE* sorted_tree_find_child( TREE_NODE *node, char* key )
|
||||
{
|
||||
TREE_NODE *next = NULL;
|
||||
int i, result;
|
||||
|
||||
if ( !node ) {
|
||||
DEBUG(0,("sorted_tree_find_child: NULL node passed into function!\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( !key ) {
|
||||
DEBUG(0,("sorted_tree_find_child: NULL key string passed into function!\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for ( i=0; i<node->num_children; i++ )
|
||||
{
|
||||
DEBUG(11,("sorted_tree_find_child: child key => [%s]\n",
|
||||
node->children[i]->key));
|
||||
|
||||
result = StrCaseCmp( node->children[i]->key, key );
|
||||
|
||||
if ( result == 0 )
|
||||
next = node->children[i];
|
||||
|
||||
/* if result > 0 then we've gone to far because
|
||||
the list of children is sorted by key name
|
||||
If result == 0, then we have a match */
|
||||
|
||||
if ( result > 0 )
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG(11,("sorted_tree_find_child: %s [%s]\n",
|
||||
next ? "Found" : "Did not find", key ));
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
Add a new node into the tree given a key path and a blob of data
|
||||
*************************************************************************/
|
||||
|
||||
BOOL sorted_tree_add( SORTED_TREE *tree, const char *path, void *data_p )
|
||||
{
|
||||
char *str, *base, *path2;
|
||||
TREE_NODE *current, *next;
|
||||
BOOL ret = True;
|
||||
|
||||
DEBUG(8,("sorted_tree_add: Enter\n"));
|
||||
|
||||
if ( !path || *path != '/' ) {
|
||||
DEBUG(0,("sorted_tree_add: Attempt to add a node with a bad path [%s]\n",
|
||||
path ? path : "NULL" ));
|
||||
return False;
|
||||
}
|
||||
|
||||
if ( !tree ) {
|
||||
DEBUG(0,("sorted_tree_add: Attempt to add a node to an uninitialized tree!\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* move past the first '/' */
|
||||
|
||||
path++;
|
||||
path2 = strdup( path );
|
||||
if ( !path2 ) {
|
||||
DEBUG(0,("sorted_tree_add: strdup() failed on string [%s]!?!?!\n", path));
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* this works sort of like a 'mkdir -p' call, possibly
|
||||
* creating an entire path to the new node at once
|
||||
* The path should be of the form /<key1>/<key2>/...
|
||||
*/
|
||||
|
||||
base = path2;
|
||||
str = path2;
|
||||
current = tree->root;
|
||||
|
||||
do {
|
||||
/* break off the remaining part of the path */
|
||||
|
||||
str = strchr( str, '/' );
|
||||
if ( str )
|
||||
*str = '\0';
|
||||
|
||||
/* iterate to the next child--birth it if necessary */
|
||||
|
||||
next = sorted_tree_find_child( current, base );
|
||||
if ( !next ) {
|
||||
next = sorted_tree_birth_child( current, base );
|
||||
if ( !next ) {
|
||||
DEBUG(0,("sorted_tree_add: Failed to create new child!\n"));
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
current = next;
|
||||
|
||||
/* setup the next part of the path */
|
||||
|
||||
base = str;
|
||||
if ( base ) {
|
||||
*base = '/';
|
||||
base++;
|
||||
str = base;
|
||||
}
|
||||
|
||||
} while ( base != NULL );
|
||||
|
||||
current->data_p = data_p;
|
||||
|
||||
DEBUG(10,("sorted_tree_add: Successfully added node [%s] to tree\n",
|
||||
path ));
|
||||
|
||||
DEBUG(8,("sorted_tree_add: Exit\n"));
|
||||
|
||||
done:
|
||||
SAFE_FREE( path2 );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
Recursive routine to print out all children of a TREE_NODE
|
||||
*************************************************************************/
|
||||
|
||||
static void sorted_tree_print_children( TREE_NODE *node, int debug, char *path )
|
||||
{
|
||||
int i;
|
||||
int num_children;
|
||||
pstring path2;
|
||||
|
||||
if ( !node )
|
||||
return;
|
||||
|
||||
|
||||
if ( node->key )
|
||||
DEBUG(debug,("%s: [%s] (%s)\n", path ? path : "NULL", node->key,
|
||||
node->data_p ? "data" : "NULL" ));
|
||||
|
||||
*path2 = '\0';
|
||||
if ( path )
|
||||
pstrcpy( path2, path );
|
||||
pstrcat( path2, node->key ? node->key : "NULL" );
|
||||
pstrcat( path2, "/" );
|
||||
|
||||
num_children = node->num_children;
|
||||
for ( i=0; i<num_children; i++ )
|
||||
sorted_tree_print_children( node->children[i], debug, path2 );
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
Dump the kys for a tree to the log file
|
||||
*************************************************************************/
|
||||
|
||||
void sorted_tree_print_keys( SORTED_TREE *tree, int debug )
|
||||
{
|
||||
int i;
|
||||
int num_children = tree->root->num_children;
|
||||
|
||||
if ( tree->root->key )
|
||||
DEBUG(debug,("ROOT/: [%s] (%s)\n", tree->root->key,
|
||||
tree->root->data_p ? "data" : "NULL" ));
|
||||
|
||||
for ( i=0; i<num_children; i++ ) {
|
||||
sorted_tree_print_children( tree->root->children[i], debug,
|
||||
tree->root->key ? tree->root->key : "ROOT/" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
return the data_p for for the node in tree matching the key string
|
||||
The key string is the full path. We must break it apart and walk
|
||||
the tree
|
||||
*************************************************************************/
|
||||
|
||||
void* sorted_tree_find( SORTED_TREE *tree, char *key )
|
||||
{
|
||||
char *keystr, *base, *str, *p;
|
||||
TREE_NODE *current;
|
||||
void *result = NULL;
|
||||
|
||||
DEBUG(10,("sorted_tree_find: Enter [%s]\n", key ? key : "NULL" ));
|
||||
|
||||
/* sanity checks first */
|
||||
|
||||
if ( !key ) {
|
||||
DEBUG(0,("sorted_tree_find: Attempt to search tree using NULL search string!\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( !tree ) {
|
||||
DEBUG(0,("sorted_tree_find: Attempt to search an uninitialized tree using string [%s]!\n",
|
||||
key ? key : "NULL" ));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( !tree->root )
|
||||
return NULL;
|
||||
|
||||
/* make a copy to play with */
|
||||
|
||||
if ( *key == '/' )
|
||||
keystr = strdup( key+1 );
|
||||
else
|
||||
keystr = strdup( key );
|
||||
|
||||
if ( !keystr ) {
|
||||
DEBUG(0,("sorted_tree_find: strdup() failed on string [%s]!?!?!\n", key));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* start breaking the path apart */
|
||||
|
||||
p = keystr;
|
||||
current = tree->root;
|
||||
|
||||
if ( tree->root->data_p )
|
||||
result = tree->root->data_p;
|
||||
|
||||
do
|
||||
{
|
||||
/* break off the remaining part of the path */
|
||||
|
||||
trim_tree_keypath( p, &base, &str );
|
||||
|
||||
DEBUG(11,("sorted_tree_find: [loop] base => [%s], new_path => [%s]\n",
|
||||
base, str));
|
||||
|
||||
/* iterate to the next child */
|
||||
|
||||
current = sorted_tree_find_child( current, base );
|
||||
|
||||
/*
|
||||
* the idea is that the data_p for a parent should
|
||||
* be inherited by all children, but allow it to be
|
||||
* overridden farther down
|
||||
*/
|
||||
|
||||
if ( current && current->data_p )
|
||||
result = current->data_p;
|
||||
|
||||
/* reset the path pointer 'p' to the remaining part of the key string */
|
||||
|
||||
p = str;
|
||||
|
||||
} while ( str && current );
|
||||
|
||||
/* result should be the data_p from the lowest match node in the tree */
|
||||
if ( result )
|
||||
DEBUG(11,("sorted_tree_find: Found data_p!\n"));
|
||||
|
||||
SAFE_FREE( keystr );
|
||||
|
||||
DEBUG(10,("sorted_tree_find: Exit\n"));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
49
source3/lib/popt_common.c
Normal file
49
source3/lib/popt_common.c
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Common popt routines
|
||||
|
||||
Copyright (C) Tim Potter 2001,2002
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
/* Handle -d,--debuglevel command line option */
|
||||
|
||||
static void debug_callback(poptContext con,
|
||||
enum poptCallbackReason reason,
|
||||
const struct poptOption *opt,
|
||||
const char *arg, const void *data)
|
||||
{
|
||||
extern BOOL AllowDebugChange;
|
||||
|
||||
switch(opt->val) {
|
||||
case 'd':
|
||||
if (arg) {
|
||||
DEBUGLEVEL = atoi(arg);
|
||||
AllowDebugChange = False;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct poptOption popt_common_debug[] = {
|
||||
{ NULL, 0, POPT_ARG_CALLBACK, debug_callback },
|
||||
{ "debuglevel", 'd', POPT_ARG_INT, NULL, 'd', "Set debug level",
|
||||
"DEBUGLEVEL" },
|
||||
{ 0 }
|
||||
};
|
105
source3/lib/system_smbd.c
Normal file
105
source3/lib/system_smbd.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
system call wrapper interface.
|
||||
Copyright (C) Andrew Tridgell 2002
|
||||
Copyright (C) Andrew Barteltt 2002
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file may assume linkage with smbd - for things like become_root()
|
||||
etc.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#ifndef HAVE_GETGROUPLIST
|
||||
/*
|
||||
This is a *much* faster way of getting the list of groups for a user
|
||||
without changing the current supplemenrary group list. The old
|
||||
method used getgrent() which could take 20 minutes on a really big
|
||||
network with hundeds of thousands of groups and users. The new method
|
||||
takes a couple of seconds.
|
||||
|
||||
NOTE!! this function only works if it is called as root!
|
||||
*/
|
||||
static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
|
||||
{
|
||||
gid_t *gids_saved;
|
||||
int ret, ngrp_saved;
|
||||
|
||||
/* work out how many groups we need to save */
|
||||
ngrp_saved = getgroups(0, NULL);
|
||||
if (ngrp_saved == -1) {
|
||||
/* this shouldn't happen */
|
||||
return -1;
|
||||
}
|
||||
|
||||
gids_saved = (gid_t *)malloc(sizeof(gid_t) * (ngrp_saved+1));
|
||||
if (!gids_saved) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngrp_saved = getgroups(ngrp_saved, gids_saved);
|
||||
if (ngrp_saved == -1) {
|
||||
free(gids_saved);
|
||||
/* very strange! */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (initgroups(user, gid) != 0) {
|
||||
free(gids_saved);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* this must be done to cope with systems that put the current egid in the
|
||||
return from getgroups() */
|
||||
save_re_gid();
|
||||
set_effective_gid(gid);
|
||||
setgid(gid);
|
||||
|
||||
ret = getgroups(*grpcnt, groups);
|
||||
if (ret >= 0) {
|
||||
*grpcnt = ret;
|
||||
}
|
||||
|
||||
restore_re_gid();
|
||||
|
||||
if (setgroups(ngrp_saved, gids_saved) != 0) {
|
||||
/* yikes! */
|
||||
DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n"));
|
||||
smb_panic("getgrouplist: failed to reset group list!\n");
|
||||
free(gids_saved);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(gids_saved);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
|
||||
{
|
||||
#ifdef HAVE_GETGROUPLIST
|
||||
return getgrouplist(user, gid, groups, grpcnt);
|
||||
#else
|
||||
int retval;
|
||||
become_root();
|
||||
retval = getgrouplist_internals(user, gid, groups, grpcnt);
|
||||
unbecome_root();
|
||||
#endif
|
||||
}
|
65
source3/lib/util_smbd.c
Normal file
65
source3/lib/util_smbd.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Samba utility functions, used in smbd only
|
||||
Copyright (C) Andrew Tridgell 2002
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
/*
|
||||
This function requires sys_getgrouplist - which is only
|
||||
available in smbd due to it's use of become_root() in a
|
||||
legacy systems hack.
|
||||
*/
|
||||
|
||||
/*
|
||||
return a full list of groups for a user
|
||||
|
||||
returns the number of groups the user is a member of. The return will include the
|
||||
users primary group.
|
||||
|
||||
remember to free the resulting gid_t array
|
||||
|
||||
NOTE! uses become_root() to gain correct priviages on systems
|
||||
that lack a native getgroups() call (uses initgroups and getgroups)
|
||||
*/
|
||||
int getgroups_user(const char *user, gid_t **groups)
|
||||
{
|
||||
struct passwd *pwd;
|
||||
int ngrp, max_grp;
|
||||
|
||||
pwd = getpwnam_alloc(user);
|
||||
if (!pwd) return -1;
|
||||
|
||||
max_grp = groups_max();
|
||||
(*groups) = (gid_t *)malloc(sizeof(gid_t) * max_grp);
|
||||
if (! *groups) {
|
||||
passwd_free(&pwd);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngrp = sys_getgrouplist(user, pwd->pw_gid, *groups, &max_grp);
|
||||
if (ngrp <= 0) {
|
||||
passwd_free(&pwd);
|
||||
free(*groups);
|
||||
return ngrp;
|
||||
}
|
||||
|
||||
passwd_free(&pwd);
|
||||
return ngrp;
|
||||
}
|
72
source3/lib/util_uuid.c
Normal file
72
source3/lib/util_uuid.c
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* UUID server routines
|
||||
* Copyright (C) Theodore Ts'o 1996, 1997,
|
||||
* Copyright (C) Jim McDonough 2002.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
/*
|
||||
* Offset between 15-Oct-1582 and 1-Jan-70
|
||||
*/
|
||||
#define TIME_OFFSET_HIGH 0x01B21DD2
|
||||
#define TIME_OFFSET_LOW 0x13814000
|
||||
|
||||
struct uuid {
|
||||
uint32 time_low;
|
||||
uint16 time_mid;
|
||||
uint16 time_hi_and_version;
|
||||
uint16 clock_seq;
|
||||
uint8 node[6];
|
||||
};
|
||||
|
||||
|
||||
static void uuid_pack(const struct uuid *uu, GUID *ptr)
|
||||
{
|
||||
uint8 *out = ptr->info;
|
||||
|
||||
SIVAL(out, 0, uu->time_low);
|
||||
SSVAL(out, 4, uu->time_mid);
|
||||
SSVAL(out, 6, uu->time_hi_and_version);
|
||||
SSVAL(out, 8, uu->clock_seq);
|
||||
memcpy(out+10, uu->node, 6);
|
||||
}
|
||||
|
||||
static void uuid_unpack(const GUID in, struct uuid *uu)
|
||||
{
|
||||
const uint8 *ptr = in.info;
|
||||
|
||||
uu->time_low = IVAL(ptr, 0);
|
||||
uu->time_mid = SVAL(ptr, 4);
|
||||
uu->time_hi_and_version = SVAL(ptr, 6);
|
||||
uu->clock_seq = SVAL(ptr, 8);
|
||||
memcpy(uu->node, ptr+10, 6);
|
||||
}
|
||||
|
||||
void uuid_generate_random(GUID *out)
|
||||
{
|
||||
GUID tmp;
|
||||
struct uuid uu;
|
||||
|
||||
generate_random_buffer(tmp.info, sizeof(tmp.info), True);
|
||||
uuid_unpack(tmp, &uu);
|
||||
|
||||
uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
|
||||
uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
|
||||
uuid_pack(&uu, out);
|
||||
}
|
252
source3/libsmb/namecache.c
Normal file
252
source3/libsmb/namecache.c
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
NetBIOS name cache module.
|
||||
|
||||
Copyright (C) Tim Potter, 2002
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
static BOOL done_namecache_init;
|
||||
static BOOL enable_namecache;
|
||||
static TDB_CONTEXT *namecache_tdb;
|
||||
|
||||
struct nc_value {
|
||||
time_t expiry; /* When entry expires */
|
||||
int count; /* Number of addresses */
|
||||
struct in_addr ip_list[0]; /* Address list */
|
||||
};
|
||||
|
||||
/* Initialise namecache system */
|
||||
|
||||
void namecache_enable(void)
|
||||
{
|
||||
/* Check if we have been here before, or name caching disabled
|
||||
by setting the name cache timeout to zero. */
|
||||
|
||||
if (done_namecache_init)
|
||||
return;
|
||||
|
||||
done_namecache_init = True;
|
||||
|
||||
if (lp_name_cache_timeout() == 0) {
|
||||
DEBUG(5, ("namecache_init: disabling netbios name cache\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Open namecache tdb in read/write or readonly mode */
|
||||
|
||||
namecache_tdb = tdb_open_log(
|
||||
lock_path("namecache.tdb"), 0,
|
||||
TDB_DEFAULT, O_RDWR | O_CREAT, 0644);
|
||||
|
||||
if (!namecache_tdb) {
|
||||
DEBUG(5, ("namecache_init: could not open %s\n",
|
||||
lock_path("namecache.tdb")));
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG(5, ("namecache_init: enabling netbios namecache, timeout %d "
|
||||
"seconds\n", lp_name_cache_timeout()));
|
||||
|
||||
enable_namecache = True;
|
||||
}
|
||||
|
||||
/* Return a key for a name and name type. The caller must free
|
||||
retval.dptr when finished. */
|
||||
|
||||
static TDB_DATA namecache_key(const char *name, int name_type)
|
||||
{
|
||||
TDB_DATA retval;
|
||||
char *keystr;
|
||||
|
||||
asprintf(&keystr, "%s#%02X", strupper_static(name), name_type);
|
||||
|
||||
retval.dsize = strlen(keystr) + 1;
|
||||
retval.dptr = keystr;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Return a data value for an IP list. The caller must free
|
||||
retval.dptr when finished. */
|
||||
|
||||
static TDB_DATA namecache_value(struct in_addr *ip_list, int num_names,
|
||||
time_t expiry)
|
||||
{
|
||||
TDB_DATA retval;
|
||||
struct nc_value *value;
|
||||
int size;
|
||||
|
||||
size = sizeof(struct nc_value) + sizeof(struct in_addr) *
|
||||
num_names;
|
||||
|
||||
value = (struct nc_value *)malloc(size);
|
||||
|
||||
value->expiry = expiry;
|
||||
value->count = num_names;
|
||||
|
||||
memcpy(value->ip_list, ip_list, num_names * sizeof(struct in_addr));
|
||||
|
||||
retval.dptr = (char *)value;
|
||||
retval.dsize = size;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Store a name in the name cache */
|
||||
|
||||
void namecache_store(const char *name, int name_type,
|
||||
int num_names, struct in_addr *ip_list)
|
||||
{
|
||||
TDB_DATA key, value;
|
||||
time_t expiry;
|
||||
int i;
|
||||
|
||||
if (!enable_namecache)
|
||||
return;
|
||||
|
||||
DEBUG(5, ("namecache_store: storing %d address%s for %s#%02x: ",
|
||||
num_names, num_names == 1 ? "": "es", name, name_type));
|
||||
|
||||
for (i = 0; i < num_names; i++)
|
||||
DEBUGADD(5, ("%s%s", inet_ntoa(ip_list[i]),
|
||||
i == (num_names - 1) ? "" : ", "));
|
||||
|
||||
DEBUGADD(5, ("\n"));
|
||||
|
||||
key = namecache_key(name, name_type);
|
||||
|
||||
/* Cache pdc location or dc lists for only a little while
|
||||
otherwise if we lock on to a bad DC we can potentially be
|
||||
out of action for the entire cache timeout time! */
|
||||
|
||||
if (name_type != 0x1b || name_type != 0x1c)
|
||||
expiry = time(NULL) + 10;
|
||||
else
|
||||
expiry = time(NULL) + lp_name_cache_timeout();
|
||||
|
||||
value = namecache_value(ip_list, num_names, expiry);
|
||||
|
||||
tdb_store(namecache_tdb, key, value, TDB_REPLACE);
|
||||
|
||||
free(key.dptr);
|
||||
free(value.dptr);
|
||||
}
|
||||
|
||||
/* Look up a name in the name cache. Return a mallocated list of IP
|
||||
addresses if the name is contained in the cache. */
|
||||
|
||||
BOOL namecache_fetch(const char *name, int name_type, struct in_addr **ip_list,
|
||||
int *num_names)
|
||||
{
|
||||
TDB_DATA key, value;
|
||||
struct nc_value *data;
|
||||
time_t now;
|
||||
int i;
|
||||
|
||||
if (!enable_namecache)
|
||||
return False;
|
||||
|
||||
/* Read value */
|
||||
|
||||
key = namecache_key(name, name_type);
|
||||
|
||||
value = tdb_fetch(namecache_tdb, key);
|
||||
|
||||
if (!value.dptr) {
|
||||
DEBUG(5, ("namecache_fetch: %s#%02x not found\n",
|
||||
name, name_type));
|
||||
goto done;
|
||||
}
|
||||
|
||||
data = (struct nc_value *)value.dptr;
|
||||
|
||||
/* Check expiry time */
|
||||
|
||||
now = time(NULL);
|
||||
|
||||
if (now > data->expiry) {
|
||||
|
||||
DEBUG(5, ("namecache_fetch: entry for %s#%02x expired\n",
|
||||
name, name_type));
|
||||
|
||||
tdb_delete(namecache_tdb, key);
|
||||
|
||||
value = tdb_null;
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((data->expiry - now) > lp_name_cache_timeout()) {
|
||||
|
||||
/* Someone may have changed the system time on us */
|
||||
|
||||
DEBUG(5, ("namecache_fetch: entry for %s#%02x has bad expiry\n",
|
||||
name, name_type));
|
||||
|
||||
tdb_delete(namecache_tdb, key);
|
||||
|
||||
value = tdb_null;
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Extract and return namelist */
|
||||
|
||||
*ip_list = (struct in_addr *)malloc(
|
||||
sizeof(struct in_addr) * data->count);
|
||||
|
||||
memcpy(*ip_list, data->ip_list, sizeof(struct in_addr) *
|
||||
data->count);
|
||||
|
||||
*num_names = data->count;
|
||||
|
||||
DEBUG(5, ("namecache_fetch: returning %d address%s for %s#%02x: ",
|
||||
*num_names, *num_names == 1 ? "" : "es", name, name_type));
|
||||
|
||||
for (i = 0; i < *num_names; i++)
|
||||
DEBUGADD(5, ("%s%s", inet_ntoa((*ip_list)[i]),
|
||||
i == (*num_names - 1) ? "" : ", "));
|
||||
|
||||
DEBUGADD(5, ("\n"));
|
||||
|
||||
done:
|
||||
SAFE_FREE(key.dptr);
|
||||
SAFE_FREE(value.dptr);
|
||||
|
||||
return value.dsize > 0;
|
||||
}
|
||||
|
||||
/* Flush all names from the name cache */
|
||||
|
||||
void namecache_flush(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!namecache_tdb)
|
||||
return;
|
||||
|
||||
result = tdb_traverse(namecache_tdb, tdb_traverse_delete_fn, NULL);
|
||||
|
||||
if (result == -1)
|
||||
DEBUG(5, ("namecache_flush: error deleting cache entries\n"));
|
||||
else
|
||||
DEBUG(5, ("namecache_flush: deleted %d cache entr%s\n",
|
||||
result, result == 1 ? "y" : "ies"));
|
||||
}
|
32
source3/python/py_common_proto.h
Normal file
32
source3/python/py_common_proto.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef _PY_COMMON_PROTO_H
|
||||
#define _PY_COMMON_PROTO_H
|
||||
|
||||
/* This file is automatically generated with "make proto". DO NOT EDIT */
|
||||
|
||||
|
||||
/* The following definitions come from python/py_common.c */
|
||||
|
||||
PyObject *py_werror_tuple(WERROR werror);
|
||||
PyObject *py_ntstatus_tuple(NTSTATUS ntstatus);
|
||||
void py_samba_init(void);
|
||||
PyObject *get_debuglevel(PyObject *self, PyObject *args);
|
||||
PyObject *set_debuglevel(PyObject *self, PyObject *args);
|
||||
PyObject *py_setup_logging(PyObject *self, PyObject *args, PyObject *kw);
|
||||
BOOL py_parse_creds(PyObject *creds, char **username, char **domain,
|
||||
char **password, char **errstr);
|
||||
struct cli_state *open_pipe_creds(char *server, PyObject *creds,
|
||||
char *pipe_name, char **errstr);
|
||||
BOOL get_level_value(PyObject *dict, uint32 *level);
|
||||
|
||||
/* The following definitions come from python/py_ntsec.c */
|
||||
|
||||
BOOL py_from_SID(PyObject **obj, DOM_SID *sid);
|
||||
BOOL py_to_SID(DOM_SID *sid, PyObject *obj);
|
||||
BOOL py_from_ACE(PyObject **dict, SEC_ACE *ace);
|
||||
BOOL py_to_ACE(SEC_ACE *ace, PyObject *dict);
|
||||
BOOL py_from_ACL(PyObject **dict, SEC_ACL *acl);
|
||||
BOOL py_to_ACL(SEC_ACL *acl, PyObject *dict, TALLOC_CTX *mem_ctx);
|
||||
BOOL py_from_SECDESC(PyObject **dict, SEC_DESC *sd);
|
||||
BOOL py_to_SECDESC(SEC_DESC **sd, PyObject *dict, TALLOC_CTX *mem_ctx);
|
||||
|
||||
#endif /* _PY_COMMON_PROTO_H */
|
462
source3/python/py_lsa.c
Normal file
462
source3/python/py_lsa.c
Normal file
@ -0,0 +1,462 @@
|
||||
/*
|
||||
Python wrappers for DCERPC/SMB client routines.
|
||||
|
||||
Copyright (C) Tim Potter, 2002
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "python/py_lsa.h"
|
||||
|
||||
PyObject *new_lsa_policy_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
|
||||
POLICY_HND *pol)
|
||||
{
|
||||
lsa_policy_hnd_object *o;
|
||||
|
||||
o = PyObject_New(lsa_policy_hnd_object, &lsa_policy_hnd_type);
|
||||
|
||||
o->cli = cli;
|
||||
o->mem_ctx = mem_ctx;
|
||||
memcpy(&o->pol, pol, sizeof(POLICY_HND));
|
||||
|
||||
return (PyObject*)o;
|
||||
}
|
||||
|
||||
/*
|
||||
* Exceptions raised by this module
|
||||
*/
|
||||
|
||||
PyObject *lsa_error; /* This indicates a non-RPC related error
|
||||
such as name lookup failure */
|
||||
|
||||
PyObject *lsa_ntstatus; /* This exception is raised when a RPC call
|
||||
returns a status code other than
|
||||
NT_STATUS_OK */
|
||||
|
||||
/*
|
||||
* Open/close lsa handles
|
||||
*/
|
||||
|
||||
static PyObject *lsa_open_policy(PyObject *self, PyObject *args,
|
||||
PyObject *kw)
|
||||
{
|
||||
static char *kwlist[] = { "servername", "creds", "access", NULL };
|
||||
char *server, *errstr;
|
||||
PyObject *creds = NULL, *result = NULL;
|
||||
uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
|
||||
struct cli_state *cli = NULL;
|
||||
NTSTATUS ntstatus;
|
||||
TALLOC_CTX *mem_ctx = NULL;
|
||||
POLICY_HND hnd;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(
|
||||
args, kw, "s|Oi", kwlist, &server, &creds, &desired_access))
|
||||
return NULL;
|
||||
|
||||
if (creds && creds != Py_None && !PyDict_Check(creds)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"credentials must be dictionary or None");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (server[0] != '\\' || server[1] != '\\') {
|
||||
PyErr_SetString(PyExc_ValueError, "UNC name required");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
server += 2;
|
||||
|
||||
if (!(cli = open_pipe_creds(server, creds, PIPE_LSARPC, &errstr))) {
|
||||
PyErr_SetString(lsa_error, errstr);
|
||||
free(errstr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(mem_ctx = talloc_init())) {
|
||||
PyErr_SetString(lsa_error, "unable to init talloc context\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
ntstatus = cli_lsa_open_policy(cli, mem_ctx, True,
|
||||
SEC_RIGHTS_MAXIMUM_ALLOWED, &hnd);
|
||||
|
||||
if (!NT_STATUS_IS_OK(ntstatus)) {
|
||||
PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
|
||||
goto done;
|
||||
}
|
||||
|
||||
result = new_lsa_policy_hnd_object(cli, mem_ctx, &hnd);
|
||||
|
||||
done:
|
||||
if (!result) {
|
||||
if (cli)
|
||||
cli_shutdown(cli);
|
||||
|
||||
if (mem_ctx)
|
||||
talloc_destroy(mem_ctx);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject *lsa_close(PyObject *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
PyObject *po;
|
||||
lsa_policy_hnd_object *hnd;
|
||||
NTSTATUS result;
|
||||
|
||||
/* Parse parameters */
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O!", &lsa_policy_hnd_type, &po))
|
||||
return NULL;
|
||||
|
||||
hnd = (lsa_policy_hnd_object *)po;
|
||||
|
||||
/* Call rpc function */
|
||||
|
||||
result = cli_lsa_close(hnd->cli, hnd->mem_ctx, &hnd->pol);
|
||||
|
||||
/* Cleanup samba stuff */
|
||||
|
||||
cli_shutdown(hnd->cli);
|
||||
talloc_destroy(hnd->mem_ctx);
|
||||
|
||||
/* Return value */
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *lsa_lookup_names(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *py_names, *result;
|
||||
NTSTATUS ntstatus;
|
||||
lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
|
||||
int num_names, i;
|
||||
const char **names;
|
||||
DOM_SID *sids;
|
||||
uint32 *name_types;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &py_names))
|
||||
return NULL;
|
||||
|
||||
if (!PyList_Check(py_names) && !PyString_Check(py_names)) {
|
||||
PyErr_SetString(PyExc_TypeError, "must be list or string");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (PyList_Check(py_names)) {
|
||||
|
||||
/* Convert list to char ** array */
|
||||
|
||||
num_names = PyList_Size(py_names);
|
||||
names = (const char **)talloc(
|
||||
hnd->mem_ctx, num_names * sizeof(char *));
|
||||
|
||||
for (i = 0; i < num_names; i++) {
|
||||
PyObject *obj = PyList_GetItem(py_names, i);
|
||||
|
||||
names[i] = talloc_strdup(hnd->mem_ctx, PyString_AsString(obj));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* Just a single element */
|
||||
|
||||
num_names = 1;
|
||||
names = (const char **)talloc(hnd->mem_ctx, sizeof(char *));
|
||||
|
||||
names[0] = PyString_AsString(py_names);
|
||||
}
|
||||
|
||||
ntstatus = cli_lsa_lookup_names(hnd->cli, hnd->mem_ctx, &hnd->pol,
|
||||
num_names, names, &sids, &name_types);
|
||||
|
||||
if (!NT_STATUS_IS_OK(ntstatus) && NT_STATUS_V(ntstatus) != 0x107) {
|
||||
PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = PyList_New(num_names);
|
||||
|
||||
for (i = 0; i < num_names; i++) {
|
||||
PyObject *sid_obj, *obj;
|
||||
|
||||
py_from_SID(&sid_obj, &sids[i]);
|
||||
|
||||
obj = Py_BuildValue("(Oi)", sid_obj, name_types[i]);
|
||||
|
||||
PyList_SetItem(result, i, obj);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject *lsa_lookup_sids(PyObject *self, PyObject *args,
|
||||
PyObject *kw)
|
||||
{
|
||||
PyObject *py_sids, *result;
|
||||
NTSTATUS ntstatus;
|
||||
int num_sids, i;
|
||||
char **domains, **names;
|
||||
uint32 *types;
|
||||
lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
|
||||
DOM_SID *sids;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &py_sids))
|
||||
return NULL;
|
||||
|
||||
if (!PyList_Check(py_sids) && !PyString_Check(py_sids)) {
|
||||
PyErr_SetString(PyExc_TypeError, "must be list or string");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (PyList_Check(py_sids)) {
|
||||
|
||||
/* Convert dictionary to char ** array */
|
||||
|
||||
num_sids = PyList_Size(py_sids);
|
||||
sids = (DOM_SID *)talloc(hnd->mem_ctx, num_sids * sizeof(DOM_SID));
|
||||
|
||||
memset(sids, 0, num_sids * sizeof(DOM_SID));
|
||||
|
||||
for (i = 0; i < num_sids; i++) {
|
||||
PyObject *obj = PyList_GetItem(py_sids, i);
|
||||
|
||||
string_to_sid(&sids[i], PyString_AsString(obj));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* Just a single element */
|
||||
|
||||
num_sids = 1;
|
||||
sids = (DOM_SID *)talloc(hnd->mem_ctx, sizeof(DOM_SID));
|
||||
|
||||
string_to_sid(&sids[0], PyString_AsString(py_sids));
|
||||
}
|
||||
|
||||
ntstatus = cli_lsa_lookup_sids(hnd->cli, hnd->mem_ctx, &hnd->pol,
|
||||
num_sids, sids, &domains, &names,
|
||||
&types);
|
||||
|
||||
if (!NT_STATUS_IS_OK(ntstatus)) {
|
||||
PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = PyList_New(num_sids);
|
||||
|
||||
for (i = 0; i < num_sids; i++) {
|
||||
PyObject *obj;
|
||||
|
||||
obj = Py_BuildValue("{sssssi}", "username", names[i],
|
||||
"domain", domains[i], "name_type",
|
||||
types[i]);
|
||||
|
||||
PyList_SetItem(result, i, obj);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject *lsa_enum_trust_dom(PyObject *self, PyObject *args)
|
||||
{
|
||||
lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
|
||||
NTSTATUS ntstatus;
|
||||
uint32 enum_ctx = 0, num_domains, i;
|
||||
char **domain_names;
|
||||
DOM_SID *domain_sids;
|
||||
PyObject *result;
|
||||
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
ntstatus = cli_lsa_enum_trust_dom(
|
||||
hnd->cli, hnd->mem_ctx, &hnd->pol, &enum_ctx,
|
||||
&num_domains, &domain_names, &domain_sids);
|
||||
|
||||
if (!NT_STATUS_IS_OK(ntstatus)) {
|
||||
PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = PyList_New(num_domains);
|
||||
|
||||
for (i = 0; i < num_domains; i++) {
|
||||
fstring sid_str;
|
||||
|
||||
sid_to_string(sid_str, &domain_sids[i]);
|
||||
PyList_SetItem(
|
||||
result, i,
|
||||
Py_BuildValue("(ss)", domain_names[i], sid_str));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Method dispatch tables
|
||||
*/
|
||||
|
||||
static PyMethodDef lsa_hnd_methods[] = {
|
||||
|
||||
/* SIDs<->names */
|
||||
|
||||
{ "lookup_sids", (PyCFunction)lsa_lookup_sids,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"Convert sids to names." },
|
||||
|
||||
{ "lookup_names", (PyCFunction)lsa_lookup_names,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"Convert names to sids." },
|
||||
|
||||
/* Trusted domains */
|
||||
|
||||
{ "enum_trusted_domains", (PyCFunction)lsa_enum_trust_dom,
|
||||
METH_VARARGS,
|
||||
"Enumerate trusted domains." },
|
||||
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static void py_lsa_policy_hnd_dealloc(PyObject* self)
|
||||
{
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
static PyObject *py_lsa_policy_hnd_getattr(PyObject *self, char *attrname)
|
||||
{
|
||||
return Py_FindMethod(lsa_hnd_methods, self, attrname);
|
||||
}
|
||||
|
||||
PyTypeObject lsa_policy_hnd_type = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0,
|
||||
"LSA Policy Handle",
|
||||
sizeof(lsa_policy_hnd_object),
|
||||
0,
|
||||
py_lsa_policy_hnd_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
py_lsa_policy_hnd_getattr, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash */
|
||||
};
|
||||
|
||||
static PyMethodDef lsa_methods[] = {
|
||||
|
||||
/* Open/close lsa handles */
|
||||
|
||||
{ "open_policy", (PyCFunction)lsa_open_policy,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"Open a policy handle" },
|
||||
|
||||
{ "close", (PyCFunction)lsa_close,
|
||||
METH_VARARGS,
|
||||
"Close a policy handle" },
|
||||
|
||||
/* Other stuff - this should really go into a samba config module
|
||||
but for the moment let's leave it here. */
|
||||
|
||||
{ "setup_logging", (PyCFunction)py_setup_logging,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"Set up debug logging.
|
||||
|
||||
Initialises Samba's debug logging system. One argument is expected which
|
||||
is a boolean specifying whether debugging is interactive and sent to stdout
|
||||
or logged to a file.
|
||||
|
||||
Example:
|
||||
|
||||
>>> spoolss.setup_logging(interactive = 1)" },
|
||||
|
||||
{ "get_debuglevel", (PyCFunction)get_debuglevel,
|
||||
METH_VARARGS,
|
||||
"Set the current debug level.
|
||||
|
||||
Example:
|
||||
|
||||
>>> spoolss.get_debuglevel()
|
||||
0" },
|
||||
|
||||
{ "set_debuglevel", (PyCFunction)set_debuglevel,
|
||||
METH_VARARGS,
|
||||
"Get the current debug level.
|
||||
|
||||
Example:
|
||||
|
||||
>>> spoolss.set_debuglevel(10)" },
|
||||
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static struct const_vals {
|
||||
char *name;
|
||||
uint32 value;
|
||||
} module_const_vals[] = {
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static void const_init(PyObject *dict)
|
||||
{
|
||||
struct const_vals *tmp;
|
||||
PyObject *obj;
|
||||
|
||||
for (tmp = module_const_vals; tmp->name; tmp++) {
|
||||
obj = PyInt_FromLong(tmp->value);
|
||||
PyDict_SetItemString(dict, tmp->name, obj);
|
||||
Py_DECREF(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Module initialisation
|
||||
*/
|
||||
|
||||
void initlsa(void)
|
||||
{
|
||||
PyObject *module, *dict;
|
||||
|
||||
/* Initialise module */
|
||||
|
||||
module = Py_InitModule("lsa", lsa_methods);
|
||||
dict = PyModule_GetDict(module);
|
||||
|
||||
lsa_error = PyErr_NewException("lsa.error", NULL, NULL);
|
||||
PyDict_SetItemString(dict, "error", lsa_error);
|
||||
|
||||
lsa_ntstatus = PyErr_NewException("lsa.ntstatus", NULL, NULL);
|
||||
PyDict_SetItemString(dict, "ntstatus", lsa_ntstatus);
|
||||
|
||||
/* Initialise policy handle object */
|
||||
|
||||
lsa_policy_hnd_type.ob_type = &PyType_Type;
|
||||
|
||||
/* Initialise constants */
|
||||
|
||||
const_init(dict);
|
||||
|
||||
/* Do samba initialisation */
|
||||
|
||||
py_samba_init();
|
||||
|
||||
setup_logging("lsa", True);
|
||||
DEBUGLEVEL = 10;
|
||||
}
|
224
source3/python/py_smb.c
Normal file
224
source3/python/py_smb.c
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
Python wrappers for DCERPC/SMB client routines.
|
||||
|
||||
Copyright (C) Tim Potter, 2002
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "python/py_smb.h"
|
||||
|
||||
/* Create a new cli_state python object */
|
||||
|
||||
PyObject *new_cli_state_object(struct cli_state *cli)
|
||||
{
|
||||
cli_state_object *o;
|
||||
|
||||
o = PyObject_New(cli_state_object, &cli_state_type);
|
||||
|
||||
o->cli = cli;
|
||||
|
||||
return (PyObject*)o;
|
||||
}
|
||||
|
||||
static PyObject *py_smb_connect(PyObject *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
static char *kwlist[] = { "server", NULL };
|
||||
struct cli_state *cli;
|
||||
char *server;
|
||||
struct in_addr ip;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &server))
|
||||
return NULL;
|
||||
|
||||
if (!(cli = cli_initialise(NULL)))
|
||||
return NULL;
|
||||
|
||||
ZERO_STRUCT(ip);
|
||||
|
||||
if (!cli_connect(cli, server, &ip))
|
||||
return NULL;
|
||||
|
||||
return new_cli_state_object(cli);
|
||||
}
|
||||
|
||||
static PyObject *py_smb_session_request(PyObject *self, PyObject *args,
|
||||
PyObject *kw)
|
||||
{
|
||||
cli_state_object *cli = (cli_state_object *)self;
|
||||
static char *kwlist[] = { "called", "calling", NULL };
|
||||
char *calling_name = NULL, *called_name;
|
||||
struct nmb_name calling, called;
|
||||
extern pstring global_myname;
|
||||
BOOL result;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|s", kwlist, &called_name,
|
||||
&calling_name))
|
||||
return NULL;
|
||||
|
||||
if (!calling_name)
|
||||
calling_name = global_myname;
|
||||
|
||||
make_nmb_name(&calling, calling_name, 0x00);
|
||||
make_nmb_name(&called, called_name, 0x20);
|
||||
|
||||
result = cli_session_request(cli->cli, &calling, &called);
|
||||
|
||||
return Py_BuildValue("i", result);
|
||||
}
|
||||
|
||||
static PyObject *py_smb_negprot(PyObject *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
cli_state_object *cli = (cli_state_object *)self;
|
||||
static char *kwlist[] = { NULL };
|
||||
BOOL result;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
|
||||
return NULL;
|
||||
|
||||
result = cli_negprot(cli->cli);
|
||||
|
||||
return Py_BuildValue("i", result);
|
||||
}
|
||||
|
||||
static PyObject *py_smb_session_setup(PyObject *self, PyObject *args,
|
||||
PyObject *kw)
|
||||
{
|
||||
cli_state_object *cli = (cli_state_object *)self;
|
||||
static char *kwlist[] = { "creds" };
|
||||
PyObject *creds;
|
||||
char *username, *domain, *password, *errstr;
|
||||
BOOL result;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "O", kwlist, &creds))
|
||||
return NULL;
|
||||
|
||||
if (!py_parse_creds(creds, &username, &domain, &password, &errstr)) {
|
||||
free(errstr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = cli_session_setup(
|
||||
cli->cli, username, password, strlen(password) + 1,
|
||||
password, strlen(password) + 1, domain);
|
||||
|
||||
return Py_BuildValue("i", result);
|
||||
}
|
||||
|
||||
static PyObject *py_smb_tconx(PyObject *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
cli_state_object *cli = (cli_state_object *)self;
|
||||
static char *kwlist[] = { "service", "creds" };
|
||||
PyObject *creds;
|
||||
char *service, *username, *domain, *password, *errstr;
|
||||
BOOL result;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "sO", kwlist, &service,
|
||||
&creds))
|
||||
return NULL;
|
||||
|
||||
if (!py_parse_creds(creds, &username, &domain, &password, &errstr)) {
|
||||
free(errstr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = cli_send_tconX(
|
||||
cli->cli, service, strequal(service, "IPC$") ? "IPC" : "?????",
|
||||
password, strlen(password) + 1);
|
||||
|
||||
return Py_BuildValue("i", result);
|
||||
}
|
||||
|
||||
static PyMethodDef smb_hnd_methods[] = {
|
||||
|
||||
{ "session_request", (PyCFunction)py_smb_session_request,
|
||||
METH_VARARGS | METH_KEYWORDS, "Request a session" },
|
||||
|
||||
{ "negprot", (PyCFunction)py_smb_negprot,
|
||||
METH_VARARGS | METH_KEYWORDS, "Protocol negotiation" },
|
||||
|
||||
{ "session_setup", (PyCFunction)py_smb_session_setup,
|
||||
METH_VARARGS | METH_KEYWORDS, "Session setup" },
|
||||
|
||||
{ "tconx", (PyCFunction)py_smb_tconx,
|
||||
METH_VARARGS | METH_KEYWORDS, "Tree connect" },
|
||||
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
/*
|
||||
* Method dispatch tables
|
||||
*/
|
||||
|
||||
static PyMethodDef smb_methods[] = {
|
||||
|
||||
{ "connect", (PyCFunction)py_smb_connect, METH_VARARGS | METH_KEYWORDS,
|
||||
"Connect to a host" },
|
||||
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static void py_cli_state_dealloc(PyObject* self)
|
||||
{
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
static PyObject *py_cli_state_getattr(PyObject *self, char *attrname)
|
||||
{
|
||||
return Py_FindMethod(smb_hnd_methods, self, attrname);
|
||||
}
|
||||
|
||||
PyTypeObject cli_state_type = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0,
|
||||
"SMB client connection",
|
||||
sizeof(cli_state_object),
|
||||
0,
|
||||
py_cli_state_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
py_cli_state_getattr, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash */
|
||||
};
|
||||
|
||||
/*
|
||||
* Module initialisation
|
||||
*/
|
||||
|
||||
void initsmb(void)
|
||||
{
|
||||
PyObject *module, *dict;
|
||||
|
||||
/* Initialise module */
|
||||
|
||||
module = Py_InitModule("smb", smb_methods);
|
||||
dict = PyModule_GetDict(module);
|
||||
|
||||
/* Initialise policy handle object */
|
||||
|
||||
cli_state_type.ob_type = &PyType_Type;
|
||||
|
||||
/* Do samba initialisation */
|
||||
|
||||
py_samba_init();
|
||||
|
||||
setup_logging("smb", True);
|
||||
DEBUGLEVEL = 10;
|
||||
}
|
42
source3/python/py_smb.h
Normal file
42
source3/python/py_smb.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Python wrappers for DCERPC/SMB client routines.
|
||||
|
||||
Copyright (C) Tim Potter, 2002
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _PY_SMB_H
|
||||
#define _PY_SMB_H
|
||||
|
||||
#include "includes.h"
|
||||
#include "Python.h"
|
||||
|
||||
#include "python/py_common_proto.h"
|
||||
|
||||
/* cli_state handle object */
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
struct cli_state *cli;
|
||||
} cli_state_object;
|
||||
|
||||
/* Exceptions raised by this module */
|
||||
|
||||
extern PyTypeObject cli_state_type;
|
||||
|
||||
extern PyObject *smb_ntstatus;
|
||||
|
||||
#endif /* _PY_SMB_H */
|
112
source3/registry/reg_cachehook.c
Normal file
112
source3/registry/reg_cachehook.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* RPC Pipe client / server routines
|
||||
* Copyright (C) Gerald Carter 2002.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* Implementation of registry hook cache tree */
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
static SORTED_TREE *cache_tree;
|
||||
extern REGISTRY_OPS regdb_ops; /* these are the default */
|
||||
static REGISTRY_HOOK default_hook = { KEY_TREE_ROOT, ®db_ops };
|
||||
|
||||
/**********************************************************************
|
||||
Initialize the cache tree
|
||||
*********************************************************************/
|
||||
|
||||
BOOL reghook_cache_init( void )
|
||||
{
|
||||
cache_tree = sorted_tree_init( &default_hook, NULL, NULL );
|
||||
|
||||
return ( cache_tree == NULL );
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Add a new REGISTRY_HOOK to the cache. Note that the keyname
|
||||
is not in the exact format that a SORTED_TREE expects.
|
||||
*********************************************************************/
|
||||
|
||||
BOOL reghook_cache_add( REGISTRY_HOOK *hook )
|
||||
{
|
||||
pstring key;
|
||||
|
||||
if ( !hook )
|
||||
return False;
|
||||
|
||||
pstrcpy( key, "\\");
|
||||
pstrcat( key, hook->keyname );
|
||||
|
||||
pstring_sub( key, "\\", "/" );
|
||||
|
||||
DEBUG(10,("reghook_cache_add: Adding key [%s]\n", key));
|
||||
|
||||
return sorted_tree_add( cache_tree, key, hook );
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Initialize the cache tree
|
||||
*********************************************************************/
|
||||
|
||||
REGISTRY_HOOK* reghook_cache_find( char *keyname )
|
||||
{
|
||||
char *key;
|
||||
int len;
|
||||
REGISTRY_HOOK *hook;
|
||||
|
||||
if ( !keyname )
|
||||
return NULL;
|
||||
|
||||
/* prepend the string with a '\' character */
|
||||
|
||||
len = strlen( keyname );
|
||||
if ( !(key = malloc( len + 2 )) ) {
|
||||
DEBUG(0,("reghook_cache_find: malloc failed for string [%s] !?!?!\n",
|
||||
keyname));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*key = '\\';
|
||||
strncpy( key+1, keyname, len+1);
|
||||
|
||||
/* swap to a form understood by the SORTED_TREE */
|
||||
|
||||
string_sub( key, "\\", "/", 0 );
|
||||
|
||||
DEBUG(10,("reghook_cache_find: Searching for keyname [%s]\n", key));
|
||||
|
||||
hook = sorted_tree_find( cache_tree, key ) ;
|
||||
|
||||
SAFE_FREE( key );
|
||||
|
||||
return hook;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Initialize the cache tree
|
||||
*********************************************************************/
|
||||
|
||||
void reghook_dump_cache( int debuglevel )
|
||||
{
|
||||
DEBUG(debuglevel,("reghook_dump_cache: Starting cache dump now...\n"));
|
||||
|
||||
sorted_tree_print_keys( cache_tree, debuglevel );
|
||||
}
|
311
source3/registry/reg_db.c
Normal file
311
source3/registry/reg_db.c
Normal file
@ -0,0 +1,311 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* RPC Pipe client / server routines
|
||||
* Copyright (C) Gerald Carter 2002.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* Implementation of internal registry database functions. */
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
static TDB_CONTEXT *tdb_reg;
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
Open the registry data in the tdb
|
||||
***********************************************************************/
|
||||
|
||||
static BOOL init_registry_data( void )
|
||||
{
|
||||
pstring keyname;
|
||||
REGSUBKEY_CTR subkeys;
|
||||
|
||||
ZERO_STRUCTP( &subkeys );
|
||||
|
||||
/* HKEY_LOCAL_MACHINE */
|
||||
|
||||
regsubkey_ctr_init( &subkeys );
|
||||
pstrcpy( keyname, KEY_HKLM );
|
||||
regsubkey_ctr_addkey( &subkeys, "SYSTEM" );
|
||||
if ( !regdb_store_reg_keys( keyname, &subkeys ))
|
||||
return False;
|
||||
regsubkey_ctr_destroy( &subkeys );
|
||||
|
||||
regsubkey_ctr_init( &subkeys );
|
||||
pstrcpy( keyname, KEY_HKLM );
|
||||
pstrcat( keyname, "/SYSTEM" );
|
||||
regsubkey_ctr_addkey( &subkeys, "CurrentControlSet" );
|
||||
if ( !regdb_store_reg_keys( keyname, &subkeys ))
|
||||
return False;
|
||||
regsubkey_ctr_destroy( &subkeys );
|
||||
|
||||
regsubkey_ctr_init( &subkeys );
|
||||
pstrcpy( keyname, KEY_HKLM );
|
||||
pstrcat( keyname, "/SYSTEM/CurrentControlSet" );
|
||||
regsubkey_ctr_addkey( &subkeys, "Control" );
|
||||
regsubkey_ctr_addkey( &subkeys, "Services" );
|
||||
if ( !regdb_store_reg_keys( keyname, &subkeys ))
|
||||
return False;
|
||||
regsubkey_ctr_destroy( &subkeys );
|
||||
|
||||
regsubkey_ctr_init( &subkeys );
|
||||
pstrcpy( keyname, KEY_HKLM );
|
||||
pstrcat( keyname, "/SYSTEM/CurrentControlSet/Control" );
|
||||
regsubkey_ctr_addkey( &subkeys, "Print" );
|
||||
regsubkey_ctr_addkey( &subkeys, "ProductOptions" );
|
||||
if ( !regdb_store_reg_keys( keyname, &subkeys ))
|
||||
return False;
|
||||
regsubkey_ctr_destroy( &subkeys );
|
||||
|
||||
pstrcpy( keyname, KEY_HKLM );
|
||||
pstrcat( keyname, "/SYSTEM/CurrentControlSet/Control/ProductOptions" );
|
||||
if ( !regdb_store_reg_keys( keyname, &subkeys ))
|
||||
return False;
|
||||
|
||||
regsubkey_ctr_init( &subkeys );
|
||||
pstrcpy( keyname, KEY_HKLM );
|
||||
pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services" );
|
||||
regsubkey_ctr_addkey( &subkeys, "Netlogon" );
|
||||
if ( !regdb_store_reg_keys( keyname, &subkeys ))
|
||||
return False;
|
||||
regsubkey_ctr_destroy( &subkeys );
|
||||
|
||||
regsubkey_ctr_init( &subkeys );
|
||||
pstrcpy( keyname, KEY_HKLM );
|
||||
pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services/Netlogon" );
|
||||
regsubkey_ctr_addkey( &subkeys, "Parameters" );
|
||||
if ( !regdb_store_reg_keys( keyname, &subkeys ))
|
||||
return False;
|
||||
regsubkey_ctr_destroy( &subkeys );
|
||||
|
||||
pstrcpy( keyname, KEY_HKLM );
|
||||
pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services/Netlogon/Parameters" );
|
||||
if ( !regdb_store_reg_keys( keyname, &subkeys ))
|
||||
return False;
|
||||
|
||||
/* HKEY_USER */
|
||||
|
||||
pstrcpy( keyname, KEY_HKU );
|
||||
if ( !regdb_store_reg_keys( keyname, &subkeys ) )
|
||||
return False;
|
||||
|
||||
/* HKEY_CLASSES_ROOT*/
|
||||
|
||||
pstrcpy( keyname, KEY_HKCR );
|
||||
if ( !regdb_store_reg_keys( keyname, &subkeys ) )
|
||||
return False;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Open the registry database
|
||||
***********************************************************************/
|
||||
|
||||
BOOL init_registry_db( void )
|
||||
{
|
||||
static pid_t local_pid;
|
||||
|
||||
if (tdb_reg && local_pid == sys_getpid())
|
||||
return True;
|
||||
|
||||
/*
|
||||
* try to open first without creating so we can determine
|
||||
* if we need to init the data in the registry
|
||||
*/
|
||||
|
||||
tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, TDB_DEFAULT, O_RDWR, 0600);
|
||||
if ( !tdb_reg )
|
||||
{
|
||||
tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
|
||||
if ( !tdb_reg ) {
|
||||
DEBUG(0,("init_registry: Failed to open registry %s (%s)\n",
|
||||
lock_path("registry.tdb"), strerror(errno) ));
|
||||
return False;
|
||||
}
|
||||
|
||||
DEBUG(10,("init_registry: Successfully created registry tdb\n"));
|
||||
|
||||
/* create the registry here */
|
||||
if ( !init_registry_data() ) {
|
||||
DEBUG(0,("init_registry: Failed to initiailize data in registry!\n"));
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
local_pid = sys_getpid();
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
Add subkey strings to the registry tdb under a defined key
|
||||
fmt is the same format as tdb_pack except this function only supports
|
||||
fstrings
|
||||
|
||||
The full path to the registry key is used as database after the
|
||||
\'s are converted to /'s. Key string is also normalized to UPPER
|
||||
case.
|
||||
***********************************************************************/
|
||||
|
||||
BOOL regdb_store_reg_keys( char *keyname, REGSUBKEY_CTR *ctr )
|
||||
{
|
||||
TDB_DATA kbuf, dbuf;
|
||||
char *buffer, *tmpbuf;
|
||||
int i = 0;
|
||||
uint32 len, buflen;
|
||||
BOOL ret = True;
|
||||
uint32 num_subkeys = regsubkey_ctr_numkeys( ctr );
|
||||
|
||||
if ( !keyname )
|
||||
return False;
|
||||
|
||||
strupper_m( keyname );
|
||||
|
||||
/* allocate some initial memory */
|
||||
|
||||
buffer = malloc(sizeof(pstring));
|
||||
buflen = sizeof(pstring);
|
||||
len = 0;
|
||||
|
||||
/* store the number of subkeys */
|
||||
|
||||
len += tdb_pack(buffer+len, buflen-len, "d", num_subkeys );
|
||||
|
||||
/* pack all the strings */
|
||||
|
||||
for (i=0; i<num_subkeys; i++) {
|
||||
len += tdb_pack( buffer+len, buflen-len, "f", regsubkey_ctr_specific_key(ctr, i) );
|
||||
if ( len > buflen ) {
|
||||
/* allocate some extra space */
|
||||
if ((tmpbuf = Realloc( buffer, len*2 )) == NULL) {
|
||||
DEBUG(0,("regdb_store_reg_keys: Failed to realloc memory of size [%d]\n", len*2));
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
buffer = tmpbuf;
|
||||
buflen = len*2;
|
||||
|
||||
len = tdb_pack( buffer+len, buflen-len, "f", regsubkey_ctr_specific_key(ctr, i) );
|
||||
}
|
||||
}
|
||||
|
||||
/* finally write out the data */
|
||||
|
||||
kbuf.dptr = keyname;
|
||||
kbuf.dsize = strlen(keyname)+1;
|
||||
dbuf.dptr = buffer;
|
||||
dbuf.dsize = len;
|
||||
if ( tdb_store( tdb_reg, kbuf, dbuf, TDB_REPLACE ) == -1) {
|
||||
ret = False;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
SAFE_FREE( buffer );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Retrieve an array of strings containing subkeys. Memory should be
|
||||
released by the caller. The subkeys are stored in a catenated string
|
||||
of null terminated character strings
|
||||
***********************************************************************/
|
||||
|
||||
int regdb_fetch_reg_keys( char* key, REGSUBKEY_CTR *ctr )
|
||||
{
|
||||
pstring path;
|
||||
uint32 num_items;
|
||||
TDB_DATA dbuf;
|
||||
char *buf;
|
||||
uint32 buflen, len;
|
||||
int i;
|
||||
fstring subkeyname;
|
||||
|
||||
DEBUG(10,("regdb_fetch_reg_keys: Enter key => [%s]\n", key ? key : "NULL"));
|
||||
|
||||
pstrcpy( path, key );
|
||||
|
||||
/* convert to key format */
|
||||
pstring_sub( path, "\\", "/" );
|
||||
strupper_m( path );
|
||||
|
||||
dbuf = tdb_fetch_by_string( tdb_reg, path );
|
||||
|
||||
buf = dbuf.dptr;
|
||||
buflen = dbuf.dsize;
|
||||
|
||||
if ( !buf ) {
|
||||
DEBUG(5,("regdb_fetch_reg_keys: tdb lookup failed to locate key [%s]\n", key));
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = tdb_unpack( buf, buflen, "d", &num_items);
|
||||
|
||||
for (i=0; i<num_items; i++) {
|
||||
len += tdb_unpack( buf+len, buflen-len, "f", subkeyname );
|
||||
regsubkey_ctr_addkey( ctr, subkeyname );
|
||||
}
|
||||
|
||||
SAFE_FREE( dbuf.dptr );
|
||||
|
||||
DEBUG(10,("regdb_fetch_reg_keys: Exit [%d] items\n", num_items));
|
||||
|
||||
return num_items;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
Retrieve an array of strings containing subkeys. Memory should be
|
||||
released by the caller. The subkeys are stored in a catenated string
|
||||
of null terminated character strings
|
||||
***********************************************************************/
|
||||
|
||||
int regdb_fetch_reg_values( char* key, REGVAL_CTR *val )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Stub function since we do not currently support storing registry
|
||||
values in the registry.tdb
|
||||
***********************************************************************/
|
||||
|
||||
BOOL regdb_store_reg_values( char *key, REGVAL_CTR *val )
|
||||
{
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Table of function pointers for default access
|
||||
*/
|
||||
|
||||
REGISTRY_OPS regdb_ops = {
|
||||
regdb_fetch_reg_keys,
|
||||
regdb_fetch_reg_values,
|
||||
regdb_store_reg_keys,
|
||||
regdb_store_reg_values
|
||||
};
|
||||
|
||||
|
575
source3/registry/reg_frontend.c
Normal file
575
source3/registry/reg_frontend.c
Normal file
@ -0,0 +1,575 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* RPC Pipe client / server routines
|
||||
* Copyright (C) Gerald Carter 2002.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* Implementation of registry frontend view functions. */
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
extern REGISTRY_OPS printing_ops;
|
||||
extern REGISTRY_OPS regdb_ops; /* these are the default */
|
||||
|
||||
/* array of REGISTRY_HOOK's which are read into a tree for easy access */
|
||||
|
||||
|
||||
REGISTRY_HOOK reg_hooks[] = {
|
||||
{ KEY_PRINTING, &printing_ops },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Utility functions for REGSUBKEY_CTR
|
||||
*/
|
||||
|
||||
/***********************************************************************
|
||||
Init the talloc context held by a REGSUBKEY_CTR structure
|
||||
**********************************************************************/
|
||||
|
||||
void regsubkey_ctr_init( REGSUBKEY_CTR *ctr )
|
||||
{
|
||||
if ( !ctr->ctx )
|
||||
ctr->ctx = talloc_init();
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Add a new key to the array
|
||||
**********************************************************************/
|
||||
|
||||
int regsubkey_ctr_addkey( REGSUBKEY_CTR *ctr, char *keyname )
|
||||
{
|
||||
uint32 len;
|
||||
char **pp;
|
||||
|
||||
if ( keyname )
|
||||
{
|
||||
len = strlen( keyname );
|
||||
|
||||
/* allocate a space for the char* in the array */
|
||||
|
||||
if ( ctr->subkeys == 0 )
|
||||
ctr->subkeys = talloc( ctr->ctx, sizeof(char*) );
|
||||
else {
|
||||
pp = talloc_realloc( ctr->ctx, ctr->subkeys, sizeof(char*)*(ctr->num_subkeys+1) );
|
||||
if ( pp )
|
||||
ctr->subkeys = pp;
|
||||
}
|
||||
|
||||
/* allocate the string and save it in the array */
|
||||
|
||||
ctr->subkeys[ctr->num_subkeys] = talloc( ctr->ctx, len+1 );
|
||||
strncpy( ctr->subkeys[ctr->num_subkeys], keyname, len+1 );
|
||||
ctr->num_subkeys++;
|
||||
}
|
||||
|
||||
return ctr->num_subkeys;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
How many keys does the container hold ?
|
||||
**********************************************************************/
|
||||
|
||||
int regsubkey_ctr_numkeys( REGSUBKEY_CTR *ctr )
|
||||
{
|
||||
return ctr->num_subkeys;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Retreive a specific key string
|
||||
**********************************************************************/
|
||||
|
||||
char* regsubkey_ctr_specific_key( REGSUBKEY_CTR *ctr, uint32 key_index )
|
||||
{
|
||||
if ( ! (key_index < ctr->num_subkeys) )
|
||||
return NULL;
|
||||
|
||||
return ctr->subkeys[key_index];
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
free memory held by a REGSUBKEY_CTR structure
|
||||
**********************************************************************/
|
||||
|
||||
void regsubkey_ctr_destroy( REGSUBKEY_CTR *ctr )
|
||||
{
|
||||
if ( ctr ) {
|
||||
talloc_destroy( ctr->ctx );
|
||||
ZERO_STRUCTP( ctr );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Utility functions for REGVAL_CTR
|
||||
*/
|
||||
|
||||
/***********************************************************************
|
||||
Init the talloc context held by a REGSUBKEY_CTR structure
|
||||
**********************************************************************/
|
||||
|
||||
void regval_ctr_init( REGVAL_CTR *ctr )
|
||||
{
|
||||
if ( !ctr->ctx )
|
||||
ctr->ctx = talloc_init();
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
How many keys does the container hold ?
|
||||
**********************************************************************/
|
||||
|
||||
int regval_ctr_numvals( REGVAL_CTR *ctr )
|
||||
{
|
||||
return ctr->num_values;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
allocate memory for and duplicate a REGISTRY_VALUE.
|
||||
This is malloc'd memory so the caller should free it when done
|
||||
**********************************************************************/
|
||||
|
||||
REGISTRY_VALUE* dup_registry_value( REGISTRY_VALUE *val )
|
||||
{
|
||||
REGISTRY_VALUE *copy = NULL;
|
||||
|
||||
if ( !val )
|
||||
return NULL;
|
||||
|
||||
if ( !(copy = malloc( sizeof(REGISTRY_VALUE) )) ) {
|
||||
DEBUG(0,("dup_registry_value: malloc() failed!\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* copy all the non-pointer initial data */
|
||||
|
||||
memcpy( copy, val, sizeof(REGISTRY_VALUE) );
|
||||
if ( val->data_p )
|
||||
{
|
||||
if ( !(copy->data_p = memdup( val->data_p, val->size )) ) {
|
||||
DEBUG(0,("dup_registry_value: memdup() failed for [%d] bytes!\n",
|
||||
val->size));
|
||||
SAFE_FREE( copy );
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
free the memory allocated to a REGISTRY_VALUE
|
||||
*********************************************************************/
|
||||
|
||||
void free_registry_value( REGISTRY_VALUE *val )
|
||||
{
|
||||
if ( !val )
|
||||
return;
|
||||
|
||||
SAFE_FREE( val->data_p );
|
||||
SAFE_FREE( val );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
*********************************************************************/
|
||||
|
||||
uint8* regval_data_p( REGISTRY_VALUE *val )
|
||||
{
|
||||
return val->data_p;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
*********************************************************************/
|
||||
|
||||
int regval_size( REGISTRY_VALUE *val )
|
||||
{
|
||||
return val->size;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
*********************************************************************/
|
||||
|
||||
char* regval_name( REGISTRY_VALUE *val )
|
||||
{
|
||||
return val->valuename;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
*********************************************************************/
|
||||
|
||||
uint32 regval_type( REGISTRY_VALUE *val )
|
||||
{
|
||||
return val->type;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Retreive a pointer to a specific value. Caller shoud dup the structure
|
||||
since this memory may go away with a regval_ctr_destroy()
|
||||
**********************************************************************/
|
||||
|
||||
REGISTRY_VALUE* regval_ctr_specific_value( REGVAL_CTR *ctr, uint32 idx )
|
||||
{
|
||||
if ( !(idx < ctr->num_values) )
|
||||
return NULL;
|
||||
|
||||
return ctr->values[idx];
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Retrive the TALLOC_CTX associated with a REGISTRY_VALUE
|
||||
**********************************************************************/
|
||||
|
||||
TALLOC_CTX* regval_ctr_getctx( REGVAL_CTR *val )
|
||||
{
|
||||
if ( !val )
|
||||
return NULL;
|
||||
|
||||
return val->ctx;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Add a new registry value to the array
|
||||
**********************************************************************/
|
||||
|
||||
int regval_ctr_addvalue( REGVAL_CTR *ctr, char *name, uint16 type,
|
||||
char *data_p, size_t size )
|
||||
{
|
||||
REGISTRY_VALUE **ppreg;
|
||||
uint16 len;
|
||||
|
||||
if ( name )
|
||||
{
|
||||
len = strlen( name );
|
||||
|
||||
/* allocate a slot in the array of pointers */
|
||||
|
||||
if ( ctr->num_values == 0 )
|
||||
ctr->values = talloc( ctr->ctx, sizeof(REGISTRY_VALUE*) );
|
||||
else {
|
||||
ppreg = talloc_realloc( ctr->ctx, ctr->values, sizeof(REGISTRY_VALUE*)*(ctr->num_values+1) );
|
||||
if ( ppreg )
|
||||
ctr->values = ppreg;
|
||||
}
|
||||
|
||||
/* allocate a new value and store the pointer in the arrya */
|
||||
|
||||
ctr->values[ctr->num_values] = talloc( ctr->ctx, sizeof(REGISTRY_VALUE) );
|
||||
|
||||
/* init the value */
|
||||
|
||||
fstrcpy( ctr->values[ctr->num_values]->valuename, name );
|
||||
ctr->values[ctr->num_values]->type = type;
|
||||
ctr->values[ctr->num_values]->data_p = talloc_memdup( ctr->ctx, data_p, size );
|
||||
ctr->values[ctr->num_values]->size = size;
|
||||
ctr->num_values++;
|
||||
}
|
||||
|
||||
return ctr->num_values;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Delete a single value from the registry container.
|
||||
No need to free memory since it is talloc'd.
|
||||
**********************************************************************/
|
||||
|
||||
int regval_ctr_delvalue( REGVAL_CTR *ctr, char *name )
|
||||
{
|
||||
int i;
|
||||
|
||||
/* search for the value */
|
||||
|
||||
for ( i=0; i<ctr->num_values; i++ ) {
|
||||
if ( strcmp( ctr->values[i]->valuename, name ) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* just return if we don't find it */
|
||||
|
||||
if ( i == ctr->num_values )
|
||||
return ctr->num_values;
|
||||
|
||||
/* just shift everything down one */
|
||||
|
||||
for ( /* use previous i */; i<(ctr->num_values-1); i++ )
|
||||
memcpy( ctr->values[i], ctr->values[i+1], sizeof(REGISTRY_VALUE) );
|
||||
|
||||
/* paranoia */
|
||||
|
||||
ZERO_STRUCTP( ctr->values[i] );
|
||||
|
||||
ctr->num_values--;
|
||||
|
||||
return ctr->num_values;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Delete a single value from the registry container.
|
||||
No need to free memory since it is talloc'd.
|
||||
**********************************************************************/
|
||||
|
||||
REGISTRY_VALUE* regval_ctr_getvalue( REGVAL_CTR *ctr, char *name )
|
||||
{
|
||||
int i;
|
||||
|
||||
/* search for the value */
|
||||
|
||||
for ( i=0; i<ctr->num_values; i++ ) {
|
||||
if ( strcmp( ctr->values[i]->valuename, name ) == 0)
|
||||
return ctr->values[i];
|
||||
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
free memory held by a REGVAL_CTR structure
|
||||
**********************************************************************/
|
||||
|
||||
void regval_ctr_destroy( REGVAL_CTR *ctr )
|
||||
{
|
||||
if ( ctr ) {
|
||||
talloc_destroy( ctr->ctx );
|
||||
ZERO_STRUCTP( ctr );
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Open the registry database and initialize the REGISTRY_HOOK cache
|
||||
***********************************************************************/
|
||||
|
||||
BOOL init_registry( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( !init_registry_db() ) {
|
||||
DEBUG(0,("init_registry: failed to initialize the registry tdb!\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* build the cache tree of registry hooks */
|
||||
|
||||
reghook_cache_init();
|
||||
|
||||
for ( i=0; reg_hooks[i].keyname; i++ ) {
|
||||
if ( !reghook_cache_add(®_hooks[i]) )
|
||||
return False;
|
||||
}
|
||||
|
||||
if ( DEBUGLEVEL >= 20 )
|
||||
reghook_dump_cache(20);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for storing registry subkeys
|
||||
***********************************************************************/
|
||||
|
||||
BOOL store_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkeys )
|
||||
{
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->store_subkeys_fn )
|
||||
return key->hook->ops->store_subkeys_fn( key->name, subkeys );
|
||||
else
|
||||
return False;
|
||||
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for storing registry values
|
||||
***********************************************************************/
|
||||
|
||||
BOOL store_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
|
||||
{
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->store_values_fn )
|
||||
return key->hook->ops->store_values_fn( key->name, val );
|
||||
else
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for enumerating registry subkeys
|
||||
Initialize the TALLOC_CTX if necessary
|
||||
***********************************************************************/
|
||||
|
||||
int fetch_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkey_ctr )
|
||||
{
|
||||
int result = -1;
|
||||
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->subkey_fn )
|
||||
result = key->hook->ops->subkey_fn( key->name, subkey_ctr );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
retreive a specific subkey specified by index. Caller is
|
||||
responsible for freeing memory
|
||||
***********************************************************************/
|
||||
|
||||
BOOL fetch_reg_keys_specific( REGISTRY_KEY *key, char** subkey, uint32 key_index )
|
||||
{
|
||||
static REGSUBKEY_CTR ctr;
|
||||
static pstring save_path;
|
||||
static BOOL ctr_init = False;
|
||||
char *s;
|
||||
|
||||
*subkey = NULL;
|
||||
|
||||
/* simple caching for performance; very basic heuristic */
|
||||
|
||||
if ( !ctr_init ) {
|
||||
DEBUG(8,("fetch_reg_keys_specific: Initializing cache of subkeys for [%s]\n", key->name));
|
||||
ZERO_STRUCTP( &ctr );
|
||||
regsubkey_ctr_init( &ctr );
|
||||
|
||||
pstrcpy( save_path, key->name );
|
||||
|
||||
if ( fetch_reg_keys( key, &ctr) == -1 )
|
||||
return False;
|
||||
|
||||
ctr_init = True;
|
||||
}
|
||||
/* clear the cache when key_index == 0 or the path has changed */
|
||||
else if ( !key_index || StrCaseCmp( save_path, key->name) ) {
|
||||
|
||||
DEBUG(8,("fetch_reg_keys_specific: Updating cache of subkeys for [%s]\n", key->name));
|
||||
|
||||
regsubkey_ctr_destroy( &ctr );
|
||||
regsubkey_ctr_init( &ctr );
|
||||
|
||||
pstrcpy( save_path, key->name );
|
||||
|
||||
if ( fetch_reg_keys( key, &ctr) == -1 )
|
||||
return False;
|
||||
}
|
||||
|
||||
if ( !(s = regsubkey_ctr_specific_key( &ctr, key_index )) )
|
||||
return False;
|
||||
|
||||
*subkey = strdup( s );
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for enumerating registry values
|
||||
Initialize the TALLOC_CTX if necessary
|
||||
***********************************************************************/
|
||||
|
||||
int fetch_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
|
||||
{
|
||||
int result = -1;
|
||||
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->value_fn )
|
||||
result = key->hook->ops->value_fn( key->name, val );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
retreive a specific subkey specified by index. Caller is
|
||||
responsible for freeing memory
|
||||
***********************************************************************/
|
||||
|
||||
BOOL fetch_reg_values_specific( REGISTRY_KEY *key, REGISTRY_VALUE **val, uint32 val_index )
|
||||
{
|
||||
static REGVAL_CTR ctr;
|
||||
static pstring save_path;
|
||||
static BOOL ctr_init = False;
|
||||
REGISTRY_VALUE *v;
|
||||
|
||||
*val = NULL;
|
||||
|
||||
/* simple caching for performance; very basic heuristic */
|
||||
|
||||
if ( !ctr_init ) {
|
||||
DEBUG(8,("fetch_reg_values_specific: Initializing cache of values for [%s]\n", key->name));
|
||||
|
||||
ZERO_STRUCTP( &ctr );
|
||||
regval_ctr_init( &ctr );
|
||||
|
||||
pstrcpy( save_path, key->name );
|
||||
|
||||
if ( fetch_reg_values( key, &ctr) == -1 )
|
||||
return False;
|
||||
|
||||
ctr_init = True;
|
||||
}
|
||||
/* clear the cache when val_index == 0 or the path has changed */
|
||||
else if ( !val_index || StrCaseCmp(save_path, key->name) ) {
|
||||
|
||||
DEBUG(8,("fetch_reg_values_specific: Updating cache of values for [%s]\n", key->name));
|
||||
|
||||
regval_ctr_destroy( &ctr );
|
||||
regval_ctr_init( &ctr );
|
||||
|
||||
pstrcpy( save_path, key->name );
|
||||
|
||||
if ( fetch_reg_values( key, &ctr) == -1 )
|
||||
return False;
|
||||
}
|
||||
|
||||
if ( !(v = regval_ctr_specific_value( &ctr, val_index )) )
|
||||
return False;
|
||||
|
||||
*val = dup_registry_value( v );
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
Utility function for splitting the base path of a registry path off
|
||||
by setting base and new_path to the apprapriate offsets withing the
|
||||
path.
|
||||
|
||||
WARNING!! Does modify the original string!
|
||||
***********************************************************************/
|
||||
|
||||
BOOL reg_split_path( char *path, char **base, char **new_path )
|
||||
{
|
||||
char *p;
|
||||
|
||||
*new_path = *base = NULL;
|
||||
|
||||
if ( !path)
|
||||
return False;
|
||||
|
||||
*base = path;
|
||||
|
||||
p = strchr( path, '\\' );
|
||||
|
||||
if ( p ) {
|
||||
*p = '\0';
|
||||
*new_path = p+1;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
|
814
source3/registry/reg_printing.c
Normal file
814
source3/registry/reg_printing.c
Normal file
@ -0,0 +1,814 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* RPC Pipe client / server routines
|
||||
* Copyright (C) Gerald Carter 2002.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* Implementation of registry virtual views for printing information */
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
#define MAX_TOP_LEVEL_KEYS 3
|
||||
|
||||
/* some symbolic indexes into the top_level_keys */
|
||||
|
||||
#define KEY_INDEX_ENVIR 0
|
||||
#define KEY_INDEX_FORMS 1
|
||||
#define KEY_INDEX_PRINTER 2
|
||||
|
||||
static char *top_level_keys[MAX_TOP_LEVEL_KEYS] = {
|
||||
"Environments",
|
||||
"Forms",
|
||||
"Printers"
|
||||
};
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
It is safe to assume that every registry path passed into on of
|
||||
the exported functions here begins with KEY_PRINTING else
|
||||
these functions would have never been called. This is a small utility
|
||||
function to strip the beginning of the path and make a copy that the
|
||||
caller can modify. Note that the caller is responsible for releasing
|
||||
the memory allocated here.
|
||||
**********************************************************************/
|
||||
|
||||
static char* trim_reg_path( char *path )
|
||||
{
|
||||
char *p;
|
||||
uint16 key_len = strlen(KEY_PRINTING);
|
||||
|
||||
/*
|
||||
* sanity check...this really should never be True.
|
||||
* It is only here to prevent us from accessing outside
|
||||
* the path buffer in the extreme case.
|
||||
*/
|
||||
|
||||
if ( strlen(path) < key_len ) {
|
||||
DEBUG(0,("trim_reg_path: Registry path too short! [%s]\n", path));
|
||||
DEBUG(0,("trim_reg_path: KEY_PRINTING => [%s]!\n", KEY_PRINTING));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
p = path + strlen( KEY_PRINTING );
|
||||
|
||||
if ( *p == '\\' )
|
||||
p++;
|
||||
|
||||
if ( *p )
|
||||
return strdup(p);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
handle enumeration of subkeys below KEY_PRINTING\Environments
|
||||
*********************************************************************/
|
||||
|
||||
static int print_subpath_environments( char *key, REGSUBKEY_CTR *subkeys )
|
||||
{
|
||||
char *environments[] = {
|
||||
"Windows 4.0",
|
||||
"Windows NT x86",
|
||||
"Windows NT R4000",
|
||||
"Windows NT Alpha_AXP",
|
||||
"Windows NT PowerPC",
|
||||
NULL };
|
||||
fstring *drivers = NULL;
|
||||
int i, env_index, num_drivers;
|
||||
BOOL valid_env = False;
|
||||
char *base, *new_path;
|
||||
char *keystr;
|
||||
char *key2 = NULL;
|
||||
int num_subkeys = -1;
|
||||
|
||||
DEBUG(10,("print_subpath_environments: key=>[%s]\n", key ? key : "NULL" ));
|
||||
|
||||
/* listed architectures of installed drivers */
|
||||
|
||||
if ( !key )
|
||||
{
|
||||
/* Windows 9x drivers */
|
||||
|
||||
if ( get_ntdrivers( &drivers, environments[0], 0 ) )
|
||||
regsubkey_ctr_addkey( subkeys, environments[0] );
|
||||
SAFE_FREE( drivers );
|
||||
|
||||
/* Windows NT/2k intel drivers */
|
||||
|
||||
if ( get_ntdrivers( &drivers, environments[1], 2 )
|
||||
|| get_ntdrivers( &drivers, environments[1], 3 ) )
|
||||
{
|
||||
regsubkey_ctr_addkey( subkeys, environments[1] );
|
||||
}
|
||||
SAFE_FREE( drivers );
|
||||
|
||||
/* Windows NT 4.0; non-intel drivers */
|
||||
for ( i=2; environments[i]; i++ ) {
|
||||
if ( get_ntdrivers( &drivers, environments[i], 2 ) )
|
||||
regsubkey_ctr_addkey( subkeys, environments[i] );
|
||||
|
||||
}
|
||||
SAFE_FREE( drivers );
|
||||
|
||||
num_subkeys = regsubkey_ctr_numkeys( subkeys );
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* we are dealing with a subkey of "Environments */
|
||||
|
||||
key2 = strdup( key );
|
||||
keystr = key2;
|
||||
reg_split_path( keystr, &base, &new_path );
|
||||
|
||||
/* sanity check */
|
||||
|
||||
for ( env_index=0; environments[env_index]; env_index++ ) {
|
||||
if ( StrCaseCmp( environments[env_index], base ) == 0 ) {
|
||||
valid_env = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !valid_env )
|
||||
return -1;
|
||||
|
||||
/* enumerate driver versions; environment is environments[env_index] */
|
||||
|
||||
if ( !new_path ) {
|
||||
switch ( env_index ) {
|
||||
case 0: /* Win9x */
|
||||
if ( get_ntdrivers( &drivers, environments[0], 0 ) ) {
|
||||
regsubkey_ctr_addkey( subkeys, "0" );
|
||||
SAFE_FREE( drivers );
|
||||
}
|
||||
break;
|
||||
case 1: /* Windows NT/2k - intel */
|
||||
if ( get_ntdrivers( &drivers, environments[1], 2 ) ) {
|
||||
regsubkey_ctr_addkey( subkeys, "2" );
|
||||
SAFE_FREE( drivers );
|
||||
}
|
||||
if ( get_ntdrivers( &drivers, environments[1], 3 ) ) {
|
||||
regsubkey_ctr_addkey( subkeys, "3" );
|
||||
SAFE_FREE( drivers );
|
||||
}
|
||||
break;
|
||||
default: /* Windows NT - nonintel */
|
||||
if ( get_ntdrivers( &drivers, environments[env_index], 2 ) ) {
|
||||
regsubkey_ctr_addkey( subkeys, "2" );
|
||||
SAFE_FREE( drivers );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
num_subkeys = regsubkey_ctr_numkeys( subkeys );
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* we finally get to enumerate the drivers */
|
||||
|
||||
keystr = new_path;
|
||||
reg_split_path( keystr, &base, &new_path );
|
||||
|
||||
if ( !new_path ) {
|
||||
num_drivers = get_ntdrivers( &drivers, environments[env_index], atoi(base) );
|
||||
for ( i=0; i<num_drivers; i++ )
|
||||
regsubkey_ctr_addkey( subkeys, drivers[i] );
|
||||
|
||||
num_subkeys = regsubkey_ctr_numkeys( subkeys );
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
SAFE_FREE( key2 );
|
||||
|
||||
return num_subkeys;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
simple function to prune a pathname down to the basename of a file
|
||||
**********************************************************************/
|
||||
|
||||
static char* dos_basename ( char *path )
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = strrchr( path, '\\' );
|
||||
if ( p )
|
||||
p++;
|
||||
else
|
||||
p = path;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
handle enumeration of values below
|
||||
KEY_PRINTING\Environments\<arch>\<version>\<drivername>
|
||||
*********************************************************************/
|
||||
|
||||
static int print_subpath_values_environments( char *key, REGVAL_CTR *val )
|
||||
{
|
||||
char *keystr;
|
||||
char *key2 = NULL;
|
||||
char *base, *new_path;
|
||||
fstring env;
|
||||
fstring driver;
|
||||
int version;
|
||||
NT_PRINTER_DRIVER_INFO_LEVEL driver_ctr;
|
||||
NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
|
||||
WERROR w_result;
|
||||
char *buffer = NULL;
|
||||
char *buffer2 = NULL;
|
||||
int buffer_size = 0;
|
||||
int i, length;
|
||||
char *filename;
|
||||
|
||||
DEBUG(8,("print_subpath_values_environments: Enter key => [%s]\n", key ? key : "NULL"));
|
||||
|
||||
if ( !key )
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The only key below KEY_PRINTING\Environments that
|
||||
* posseses values is each specific printer driver
|
||||
* First get the arch, version, & driver name
|
||||
*/
|
||||
|
||||
/* env */
|
||||
|
||||
key2 = strdup( key );
|
||||
keystr = key2;
|
||||
reg_split_path( keystr, &base, &new_path );
|
||||
if ( !base || !new_path )
|
||||
return 0;
|
||||
fstrcpy( env, base );
|
||||
|
||||
/* version */
|
||||
|
||||
keystr = new_path;
|
||||
reg_split_path( keystr, &base, &new_path );
|
||||
if ( !base || !new_path )
|
||||
return 0;
|
||||
version = atoi( base );
|
||||
|
||||
/* printer driver name */
|
||||
|
||||
keystr = new_path;
|
||||
reg_split_path( keystr, &base, &new_path );
|
||||
/* new_path should be NULL here since this must be the last key */
|
||||
if ( !base || new_path )
|
||||
return 0;
|
||||
fstrcpy( driver, base );
|
||||
|
||||
w_result = get_a_printer_driver( &driver_ctr, 3, driver, env, version );
|
||||
|
||||
if ( !W_ERROR_IS_OK(w_result) )
|
||||
return -1;
|
||||
|
||||
/* build the values out of the driver information */
|
||||
info3 = driver_ctr.info_3;
|
||||
|
||||
filename = dos_basename( info3->driverpath );
|
||||
regval_ctr_addvalue( val, "Driver", REG_SZ, filename, strlen(filename)+1 );
|
||||
filename = dos_basename( info3->configfile );
|
||||
regval_ctr_addvalue( val, "Configuration File", REG_SZ, filename, strlen(filename)+1 );
|
||||
filename = dos_basename( info3->datafile );
|
||||
regval_ctr_addvalue( val, "Data File", REG_SZ, filename, strlen(filename)+1 );
|
||||
filename = dos_basename( info3->helpfile );
|
||||
regval_ctr_addvalue( val, "Help File", REG_SZ, filename, strlen(filename)+1 );
|
||||
|
||||
regval_ctr_addvalue( val, "Data Type", REG_SZ, info3->defaultdatatype, strlen(info3->defaultdatatype)+1 );
|
||||
|
||||
regval_ctr_addvalue( val, "Version", REG_DWORD, (char*)&info3->cversion, sizeof(info3->cversion) );
|
||||
|
||||
if ( info3->dependentfiles )
|
||||
{
|
||||
/* place the list of dependent files in a single
|
||||
character buffer, separating each file name by
|
||||
a NULL */
|
||||
|
||||
for ( i=0; strcmp(info3->dependentfiles[i], ""); i++ )
|
||||
{
|
||||
/* strip the path to only the file's base name */
|
||||
|
||||
filename = dos_basename( info3->dependentfiles[i] );
|
||||
|
||||
length = strlen(filename);
|
||||
|
||||
buffer2 = Realloc( buffer, buffer_size + length + 1 );
|
||||
if ( !buffer2 )
|
||||
break;
|
||||
buffer = buffer2;
|
||||
|
||||
memcpy( buffer+buffer_size, filename, length+1 );
|
||||
|
||||
buffer_size += length + 1;
|
||||
}
|
||||
|
||||
/* terminated by double NULL. Add the final one here */
|
||||
|
||||
buffer2 = Realloc( buffer, buffer_size + 1 );
|
||||
if ( !buffer2 ) {
|
||||
SAFE_FREE( buffer );
|
||||
buffer_size = 0;
|
||||
}
|
||||
else {
|
||||
buffer = buffer2;
|
||||
buffer[buffer_size++] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
regval_ctr_addvalue( val, "Dependent Files", REG_MULTI_SZ, buffer, buffer_size );
|
||||
|
||||
free_a_printer_driver( driver_ctr, 3 );
|
||||
SAFE_FREE( key2 );
|
||||
SAFE_FREE( buffer );
|
||||
|
||||
DEBUG(8,("print_subpath_values_environments: Exit\n"));
|
||||
|
||||
return regval_ctr_numvals( val );
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
handle enumeration of subkeys below KEY_PRINTING\Forms
|
||||
Really just a stub function, but left here in case it needs to
|
||||
be expanded later on
|
||||
*********************************************************************/
|
||||
|
||||
static int print_subpath_forms( char *key, REGSUBKEY_CTR *subkeys )
|
||||
{
|
||||
DEBUG(10,("print_subpath_forms: key=>[%s]\n", key ? key : "NULL" ));
|
||||
|
||||
/* there are no subkeys */
|
||||
|
||||
if ( key )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
handle enumeration of values below KEY_PRINTING\Forms
|
||||
*********************************************************************/
|
||||
|
||||
static int print_subpath_values_forms( char *key, REGVAL_CTR *val )
|
||||
{
|
||||
int num_values = 0;
|
||||
uint32 data[8];
|
||||
int form_index = 1;
|
||||
|
||||
DEBUG(10,("print_values_forms: key=>[%s]\n", key ? key : "NULL" ));
|
||||
|
||||
/* handle ..\Forms\ */
|
||||
|
||||
if ( !key )
|
||||
{
|
||||
nt_forms_struct *forms_list = NULL;
|
||||
nt_forms_struct *form = NULL;
|
||||
int i;
|
||||
|
||||
if ( (num_values = get_ntforms( &forms_list )) == 0 )
|
||||
return 0;
|
||||
|
||||
DEBUG(10,("print_subpath_values_forms: [%d] user defined forms returned\n",
|
||||
num_values));
|
||||
|
||||
/* handle user defined forms */
|
||||
|
||||
for ( i=0; i<num_values; i++ )
|
||||
{
|
||||
form = &forms_list[i];
|
||||
|
||||
data[0] = form->width;
|
||||
data[1] = form->length;
|
||||
data[2] = form->left;
|
||||
data[3] = form->top;
|
||||
data[4] = form->right;
|
||||
data[5] = form->bottom;
|
||||
data[6] = form_index++;
|
||||
data[7] = form->flag;
|
||||
|
||||
regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
|
||||
|
||||
}
|
||||
|
||||
SAFE_FREE( forms_list );
|
||||
forms_list = NULL;
|
||||
|
||||
/* handle built-on forms */
|
||||
|
||||
if ( (num_values = get_builtin_ntforms( &forms_list )) == 0 )
|
||||
return 0;
|
||||
|
||||
DEBUG(10,("print_subpath_values_forms: [%d] built-in forms returned\n",
|
||||
num_values));
|
||||
|
||||
for ( i=0; i<num_values; i++ )
|
||||
{
|
||||
form = &forms_list[i];
|
||||
|
||||
data[0] = form->width;
|
||||
data[1] = form->length;
|
||||
data[2] = form->left;
|
||||
data[3] = form->top;
|
||||
data[4] = form->right;
|
||||
data[5] = form->bottom;
|
||||
data[6] = form_index++;
|
||||
data[7] = form->flag;
|
||||
|
||||
regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
|
||||
}
|
||||
|
||||
SAFE_FREE( forms_list );
|
||||
}
|
||||
|
||||
return num_values;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
handle enumeration of subkeys below KEY_PRINTING\Printers
|
||||
*********************************************************************/
|
||||
|
||||
static int print_subpath_printers( char *key, REGSUBKEY_CTR *subkeys )
|
||||
{
|
||||
int n_services = lp_numservices();
|
||||
int snum;
|
||||
fstring sname;
|
||||
int num_subkeys = 0;
|
||||
char *keystr, *key2 = NULL;
|
||||
char *base, *new_path;
|
||||
NT_PRINTER_INFO_LEVEL *printer = NULL;
|
||||
|
||||
|
||||
DEBUG(10,("print_subpath_printers: key=>[%s]\n", key ? key : "NULL" ));
|
||||
|
||||
if ( !key )
|
||||
{
|
||||
/* enumerate all printers */
|
||||
|
||||
for (snum=0; snum<n_services; snum++) {
|
||||
if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
|
||||
continue;
|
||||
|
||||
fstrcpy( sname, lp_servicename(snum) );
|
||||
|
||||
regsubkey_ctr_addkey( subkeys, sname );
|
||||
}
|
||||
|
||||
num_subkeys = regsubkey_ctr_numkeys( subkeys );
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* get information for a specific printer */
|
||||
|
||||
key2 = strdup( key );
|
||||
keystr = key2;
|
||||
reg_split_path( keystr, &base, &new_path );
|
||||
|
||||
|
||||
if ( !new_path ) {
|
||||
/* sanity check on the printer name */
|
||||
if ( !W_ERROR_IS_OK( get_a_printer(&printer, 2, base) ) )
|
||||
goto done;
|
||||
|
||||
free_a_printer( &printer, 2 );
|
||||
|
||||
regsubkey_ctr_addkey( subkeys, SPOOL_PRINTERDATA_KEY );
|
||||
}
|
||||
|
||||
/* no other subkeys below here */
|
||||
|
||||
done:
|
||||
SAFE_FREE( key2 );
|
||||
return num_subkeys;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
handle enumeration of values below KEY_PRINTING\Printers
|
||||
*********************************************************************/
|
||||
|
||||
static int print_subpath_values_printers( char *key, REGVAL_CTR *val )
|
||||
{
|
||||
int num_values = 0;
|
||||
char *keystr, *key2 = NULL;
|
||||
char *base, *new_path;
|
||||
NT_PRINTER_INFO_LEVEL *printer = NULL;
|
||||
NT_PRINTER_INFO_LEVEL_2 *info2;
|
||||
DEVICEMODE *devmode;
|
||||
prs_struct prs;
|
||||
uint32 offset;
|
||||
int snum;
|
||||
int i;
|
||||
fstring valuename;
|
||||
uint8 *data;
|
||||
uint32 type, data_len;
|
||||
fstring printername;
|
||||
|
||||
/*
|
||||
* There are tw cases to deal with here
|
||||
* (1) enumeration of printer_info_2 values
|
||||
* (2) enumeration of the PrinterDriverData subney
|
||||
*/
|
||||
|
||||
if ( !key ) {
|
||||
/* top level key has no values */
|
||||
goto done;
|
||||
}
|
||||
|
||||
key2 = strdup( key );
|
||||
keystr = key2;
|
||||
reg_split_path( keystr, &base, &new_path );
|
||||
|
||||
fstrcpy( printername, base );
|
||||
|
||||
if ( !new_path )
|
||||
{
|
||||
/* we are dealing with the printer itself */
|
||||
|
||||
if ( !W_ERROR_IS_OK( get_a_printer(&printer, 2, printername) ) )
|
||||
goto done;
|
||||
|
||||
info2 = printer->info_2;
|
||||
|
||||
|
||||
regval_ctr_addvalue( val, "Attributes", REG_DWORD, (char*)&info2->attributes, sizeof(info2->attributes) );
|
||||
regval_ctr_addvalue( val, "Priority", REG_DWORD, (char*)&info2->priority, sizeof(info2->attributes) );
|
||||
regval_ctr_addvalue( val, "ChangeID", REG_DWORD, (char*)&info2->changeid, sizeof(info2->changeid) );
|
||||
regval_ctr_addvalue( val, "Default Priority", REG_DWORD, (char*)&info2->default_priority, sizeof(info2->default_priority) );
|
||||
regval_ctr_addvalue( val, "Status", REG_DWORD, (char*)&info2->status, sizeof(info2->status) );
|
||||
regval_ctr_addvalue( val, "StartTime", REG_DWORD, (char*)&info2->starttime, sizeof(info2->starttime) );
|
||||
regval_ctr_addvalue( val, "UntilTime", REG_DWORD, (char*)&info2->untiltime, sizeof(info2->untiltime) );
|
||||
regval_ctr_addvalue( val, "cjobs", REG_DWORD, (char*)&info2->cjobs, sizeof(info2->cjobs) );
|
||||
regval_ctr_addvalue( val, "AveragePPM", REG_DWORD, (char*)&info2->averageppm, sizeof(info2->averageppm) );
|
||||
|
||||
regval_ctr_addvalue( val, "Name", REG_SZ, info2->printername, sizeof(info2->printername)+1 );
|
||||
regval_ctr_addvalue( val, "Location", REG_SZ, info2->location, sizeof(info2->location)+1 );
|
||||
regval_ctr_addvalue( val, "Comment", REG_SZ, info2->comment, sizeof(info2->comment)+1 );
|
||||
regval_ctr_addvalue( val, "Parameters", REG_SZ, info2->parameters, sizeof(info2->parameters)+1 );
|
||||
regval_ctr_addvalue( val, "Port", REG_SZ, info2->portname, sizeof(info2->portname)+1 );
|
||||
regval_ctr_addvalue( val, "Server", REG_SZ, info2->servername, sizeof(info2->servername)+1 );
|
||||
regval_ctr_addvalue( val, "Share", REG_SZ, info2->sharename, sizeof(info2->sharename)+1 );
|
||||
regval_ctr_addvalue( val, "Driver", REG_SZ, info2->drivername, sizeof(info2->drivername)+1 );
|
||||
regval_ctr_addvalue( val, "Separator File", REG_SZ, info2->sepfile, sizeof(info2->sepfile)+1 );
|
||||
regval_ctr_addvalue( val, "Print Processor", REG_SZ, "winprint", sizeof("winprint")+1 );
|
||||
|
||||
|
||||
/* use a prs_struct for converting the devmode and security
|
||||
descriptor to REG_BIARY */
|
||||
|
||||
prs_init( &prs, MAX_PDU_FRAG_LEN, regval_ctr_getctx(val), MARSHALL);
|
||||
|
||||
/* stream the device mode */
|
||||
|
||||
snum = lp_servicenumber(info2->sharename);
|
||||
if ( (devmode = construct_dev_mode( snum )) != NULL )
|
||||
{
|
||||
if ( spoolss_io_devmode( "devmode", &prs, 0, devmode ) ) {
|
||||
|
||||
offset = prs_offset( &prs );
|
||||
|
||||
regval_ctr_addvalue( val, "Default Devmode", REG_BINARY, prs_data_p(&prs), offset );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
prs_mem_clear( &prs );
|
||||
prs_set_offset( &prs, 0 );
|
||||
|
||||
if ( info2->secdesc_buf && info2->secdesc_buf->len )
|
||||
{
|
||||
if ( sec_io_desc("sec_desc", &info2->secdesc_buf->sec, &prs, 0 ) ) {
|
||||
|
||||
offset = prs_offset( &prs );
|
||||
|
||||
regval_ctr_addvalue( val, "Security", REG_BINARY, prs_data_p(&prs), offset );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
prs_mem_free( &prs );
|
||||
free_a_printer( &printer, 2 );
|
||||
|
||||
num_values = regval_ctr_numvals( val );
|
||||
goto done;
|
||||
|
||||
}
|
||||
|
||||
|
||||
keystr = new_path;
|
||||
reg_split_path( keystr, &base, &new_path );
|
||||
|
||||
/* here should be no more path components here */
|
||||
|
||||
if ( new_path || strcmp(base, SPOOL_PRINTERDATA_KEY) )
|
||||
goto done;
|
||||
|
||||
/* now enumerate the PrinterDriverData key */
|
||||
if ( !W_ERROR_IS_OK( get_a_printer(&printer, 2, printername) ) )
|
||||
goto done;
|
||||
|
||||
info2 = printer->info_2;
|
||||
|
||||
|
||||
/* iterate over all printer data and fill the regval container */
|
||||
|
||||
#if 0 /* JERRY */
|
||||
for ( i=0; get_specific_param_by_index(*printer, 2, i, valuename, &data, &type, &data_len); i++ )
|
||||
{
|
||||
regval_ctr_addvalue( val, valuename, type, data, data_len );
|
||||
}
|
||||
#endif
|
||||
|
||||
free_a_printer( &printer, 2 );
|
||||
|
||||
num_values = regval_ctr_numvals( val );
|
||||
|
||||
done:
|
||||
SAFE_FREE( key2 );
|
||||
|
||||
return num_values;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Routine to handle enumeration of subkeys and values
|
||||
below KEY_PRINTING (depending on whether or not subkeys/val are
|
||||
valid pointers.
|
||||
*********************************************************************/
|
||||
|
||||
static int handle_printing_subpath( char *key, REGSUBKEY_CTR *subkeys, REGVAL_CTR *val )
|
||||
{
|
||||
int result = 0;
|
||||
char *p, *base;
|
||||
int i;
|
||||
|
||||
DEBUG(10,("handle_printing_subpath: key=>[%s]\n", key ));
|
||||
|
||||
/*
|
||||
* break off the first part of the path
|
||||
* topmost base **must** be one of the strings
|
||||
* in top_level_keys[]
|
||||
*/
|
||||
|
||||
reg_split_path( key, &base, &p);
|
||||
|
||||
for ( i=0; i<MAX_TOP_LEVEL_KEYS; i++ ) {
|
||||
if ( StrCaseCmp( top_level_keys[i], base ) == 0 )
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG(10,("handle_printing_subpath: base=>[%s], i==[%d]\n", base, i));
|
||||
|
||||
if ( !(i < MAX_TOP_LEVEL_KEYS) )
|
||||
return -1;
|
||||
|
||||
/* Call routine to handle each top level key */
|
||||
switch ( i )
|
||||
{
|
||||
case KEY_INDEX_ENVIR:
|
||||
if ( subkeys )
|
||||
print_subpath_environments( p, subkeys );
|
||||
if ( val )
|
||||
print_subpath_values_environments( p, val );
|
||||
break;
|
||||
|
||||
case KEY_INDEX_FORMS:
|
||||
if ( subkeys )
|
||||
print_subpath_forms( p, subkeys );
|
||||
if ( val )
|
||||
print_subpath_values_forms( p, val );
|
||||
break;
|
||||
|
||||
case KEY_INDEX_PRINTER:
|
||||
if ( subkeys )
|
||||
print_subpath_printers( p, subkeys );
|
||||
if ( val )
|
||||
print_subpath_values_printers( p, val );
|
||||
break;
|
||||
|
||||
/* default case for top level key that has no handler */
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
/**********************************************************************
|
||||
Enumerate registry subkey names given a registry path.
|
||||
Caller is responsible for freeing memory to **subkeys
|
||||
*********************************************************************/
|
||||
|
||||
int printing_subkey_info( char *key, REGSUBKEY_CTR *subkey_ctr )
|
||||
{
|
||||
char *path;
|
||||
BOOL top_level = False;
|
||||
int num_subkeys = 0;
|
||||
|
||||
DEBUG(10,("printing_subkey_info: key=>[%s]\n", key));
|
||||
|
||||
path = trim_reg_path( key );
|
||||
|
||||
/* check to see if we are dealing with the top level key */
|
||||
|
||||
if ( !path )
|
||||
top_level = True;
|
||||
|
||||
if ( top_level ) {
|
||||
for ( num_subkeys=0; num_subkeys<MAX_TOP_LEVEL_KEYS; num_subkeys++ )
|
||||
regsubkey_ctr_addkey( subkey_ctr, top_level_keys[num_subkeys] );
|
||||
}
|
||||
else
|
||||
num_subkeys = handle_printing_subpath( path, subkey_ctr, NULL );
|
||||
|
||||
SAFE_FREE( path );
|
||||
|
||||
return num_subkeys;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Enumerate registry values given a registry path.
|
||||
Caller is responsible for freeing memory
|
||||
*********************************************************************/
|
||||
|
||||
int printing_value_info( char *key, REGVAL_CTR *val )
|
||||
{
|
||||
char *path;
|
||||
BOOL top_level = False;
|
||||
int num_values = 0;
|
||||
|
||||
DEBUG(10,("printing_value_info: key=>[%s]\n", key));
|
||||
|
||||
path = trim_reg_path( key );
|
||||
|
||||
/* check to see if we are dealing with the top level key */
|
||||
|
||||
if ( !path )
|
||||
top_level = True;
|
||||
|
||||
/* fill in values from the getprinterdata_printer_server() */
|
||||
if ( top_level )
|
||||
num_values = 0;
|
||||
else
|
||||
num_values = handle_printing_subpath( path, NULL, val );
|
||||
|
||||
|
||||
return num_values;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Stub function which always returns failure since we don't want
|
||||
people storing printing information directly via regostry calls
|
||||
(for now at least)
|
||||
*********************************************************************/
|
||||
|
||||
BOOL printing_store_subkey( char *key, REGSUBKEY_CTR *subkeys )
|
||||
{
|
||||
return False;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Stub function which always returns failure since we don't want
|
||||
people storing printing information directly via regostry calls
|
||||
(for now at least)
|
||||
*********************************************************************/
|
||||
|
||||
BOOL printing_store_value( char *key, REGVAL_CTR *val )
|
||||
{
|
||||
return False;
|
||||
}
|
||||
|
||||
/*
|
||||
* Table of function pointers for accessing printing data
|
||||
*/
|
||||
|
||||
REGISTRY_OPS printing_ops = {
|
||||
printing_subkey_info,
|
||||
printing_value_info,
|
||||
printing_store_subkey,
|
||||
printing_store_value
|
||||
};
|
||||
|
||||
|
245
source3/rpc_client/cli_dfs.c
Normal file
245
source3/rpc_client/cli_dfs.c
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
RPC pipe client
|
||||
Copyright (C) Tim Potter 2000-2001,
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
/* Query DFS support */
|
||||
|
||||
NTSTATUS cli_dfs_exist(struct cli_state *cli, TALLOC_CTX *mem_ctx,
|
||||
BOOL *dfs_exists)
|
||||
{
|
||||
prs_struct qbuf, rbuf;
|
||||
DFS_Q_DFS_EXIST q;
|
||||
DFS_R_DFS_EXIST r;
|
||||
NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
|
||||
|
||||
ZERO_STRUCT(q);
|
||||
ZERO_STRUCT(r);
|
||||
|
||||
/* Initialise parse structures */
|
||||
|
||||
prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
|
||||
prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
|
||||
|
||||
/* Marshall data and send request */
|
||||
|
||||
init_dfs_q_dfs_exist(&q);
|
||||
|
||||
if (!dfs_io_q_dfs_exist("", &q, &qbuf, 0) ||
|
||||
!rpc_api_pipe_req(cli, DFS_EXIST, &qbuf, &rbuf)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Unmarshall response */
|
||||
|
||||
if (!dfs_io_r_dfs_exist("", &r, &rbuf, 0)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Return result */
|
||||
|
||||
*dfs_exists = (r.status != 0);
|
||||
|
||||
result = NT_STATUS_OK;
|
||||
|
||||
done:
|
||||
prs_mem_free(&qbuf);
|
||||
prs_mem_free(&rbuf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NTSTATUS cli_dfs_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
|
||||
char *entrypath, char *servername, char *sharename,
|
||||
char *comment, uint32 flags)
|
||||
{
|
||||
prs_struct qbuf, rbuf;
|
||||
DFS_Q_DFS_ADD q;
|
||||
DFS_R_DFS_ADD r;
|
||||
NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
|
||||
|
||||
ZERO_STRUCT(q);
|
||||
ZERO_STRUCT(r);
|
||||
|
||||
/* Initialise parse structures */
|
||||
|
||||
prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
|
||||
prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
|
||||
|
||||
/* Marshall data and send request */
|
||||
|
||||
init_dfs_q_dfs_add(&q, entrypath, servername, sharename, comment,
|
||||
flags);
|
||||
|
||||
if (!dfs_io_q_dfs_add("", &q, &qbuf, 0) ||
|
||||
!rpc_api_pipe_req(cli, DFS_ADD, &qbuf, &rbuf)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Unmarshall response */
|
||||
|
||||
if (!dfs_io_r_dfs_add("", &r, &rbuf, 0)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Return result */
|
||||
|
||||
result = werror_to_ntstatus(r.status);
|
||||
|
||||
done:
|
||||
prs_mem_free(&qbuf);
|
||||
prs_mem_free(&rbuf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NTSTATUS cli_dfs_remove(struct cli_state *cli, TALLOC_CTX *mem_ctx,
|
||||
char *entrypath, char *servername, char *sharename)
|
||||
{
|
||||
prs_struct qbuf, rbuf;
|
||||
DFS_Q_DFS_REMOVE q;
|
||||
DFS_R_DFS_REMOVE r;
|
||||
NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
|
||||
|
||||
ZERO_STRUCT(q);
|
||||
ZERO_STRUCT(r);
|
||||
|
||||
/* Initialise parse structures */
|
||||
|
||||
prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
|
||||
prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
|
||||
|
||||
/* Marshall data and send request */
|
||||
|
||||
init_dfs_q_dfs_remove(&q, entrypath, servername, sharename);
|
||||
|
||||
if (!dfs_io_q_dfs_remove("", &q, &qbuf, 0) ||
|
||||
!rpc_api_pipe_req(cli, DFS_REMOVE, &qbuf, &rbuf)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Unmarshall response */
|
||||
|
||||
if (!dfs_io_r_dfs_remove("", &r, &rbuf, 0)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Return result */
|
||||
|
||||
result = werror_to_ntstatus(r.status);
|
||||
|
||||
done:
|
||||
prs_mem_free(&qbuf);
|
||||
prs_mem_free(&rbuf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NTSTATUS cli_dfs_get_info(struct cli_state *cli, TALLOC_CTX *mem_ctx,
|
||||
char *entrypath, char *servername, char *sharename,
|
||||
uint32 info_level, DFS_INFO_CTR *ctr)
|
||||
|
||||
{
|
||||
prs_struct qbuf, rbuf;
|
||||
DFS_Q_DFS_GET_INFO q;
|
||||
DFS_R_DFS_GET_INFO r;
|
||||
NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
|
||||
|
||||
ZERO_STRUCT(q);
|
||||
ZERO_STRUCT(r);
|
||||
|
||||
/* Initialise parse structures */
|
||||
|
||||
prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
|
||||
prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
|
||||
|
||||
/* Marshall data and send request */
|
||||
|
||||
init_dfs_q_dfs_get_info(&q, entrypath, servername, sharename,
|
||||
info_level);
|
||||
|
||||
if (!dfs_io_q_dfs_get_info("", &q, &qbuf, 0) ||
|
||||
!rpc_api_pipe_req(cli, DFS_GET_INFO, &qbuf, &rbuf)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Unmarshall response */
|
||||
|
||||
if (!dfs_io_r_dfs_get_info("", &r, &rbuf, 0)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Return result */
|
||||
|
||||
result = werror_to_ntstatus(r.status);
|
||||
*ctr = r.ctr;
|
||||
|
||||
done:
|
||||
prs_mem_free(&qbuf);
|
||||
prs_mem_free(&rbuf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Enumerate dfs shares */
|
||||
|
||||
NTSTATUS cli_dfs_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
|
||||
uint32 info_level, DFS_INFO_CTR *ctr)
|
||||
{
|
||||
prs_struct qbuf, rbuf;
|
||||
DFS_Q_DFS_ENUM q;
|
||||
DFS_R_DFS_ENUM r;
|
||||
NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
|
||||
|
||||
ZERO_STRUCT(q);
|
||||
ZERO_STRUCT(r);
|
||||
|
||||
/* Initialise parse structures */
|
||||
|
||||
prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
|
||||
prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
|
||||
|
||||
/* Marshall data and send request */
|
||||
|
||||
init_dfs_q_dfs_enum(&q, info_level, ctr);
|
||||
|
||||
if (!dfs_io_q_dfs_enum("", &q, &qbuf, 0) ||
|
||||
!rpc_api_pipe_req(cli, DFS_ENUM, &qbuf, &rbuf)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Unmarshall response */
|
||||
|
||||
r.ctr = ctr;
|
||||
|
||||
if (!dfs_io_r_dfs_enum("", &r, &rbuf, 0)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Return result */
|
||||
|
||||
result = werror_to_ntstatus(r.status);
|
||||
|
||||
done:
|
||||
prs_mem_free(&qbuf);
|
||||
prs_mem_free(&rbuf);
|
||||
|
||||
return result;
|
||||
}
|
1256
source3/rpc_client/cli_lsarpc.c
Normal file
1256
source3/rpc_client/cli_lsarpc.c
Normal file
File diff suppressed because it is too large
Load Diff
123
source3/sam/SAM-interface_handles.txt
Normal file
123
source3/sam/SAM-interface_handles.txt
Normal file
@ -0,0 +1,123 @@
|
||||
SAM API
|
||||
|
||||
NTSTATUS sam_get_sec_obj(NT_USER_TOKEN *access, DOM_SID *sid, SEC_DESC **sd)
|
||||
NTSTATUS sam_set_sec_obj(NT_USER_TOKEN *access, DOM_SID *sid, SEC_DESC *sd)
|
||||
|
||||
NTSTATUS sam_lookup_name(NT_USER_TOKEN *access, DOM_SID *domain, char *name, DOM_SID **sid, uint32 *type)
|
||||
NTSTATUS sam_lookup_sid(NT_USER_TOKEN *access, DOM_SID *sid, char **name, uint32 *type)
|
||||
|
||||
|
||||
Domain API
|
||||
|
||||
NTSTATUS sam_update_domain(SAM_DOMAIN_HANDLE *domain)
|
||||
|
||||
NTSTATUS sam_enum_domains(NT_USER_TOKEN *access, int32 *domain_count, DOM_SID **domains, char **domain_names)
|
||||
NTSTATUS sam_lookup_domain(NT_USER_TOKEN *access, char *domain, DOM_SID **domainsid)
|
||||
|
||||
NTSTATUS sam_get_domain_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *domainsid, SAM_DOMAIN_HANDLE **domain)
|
||||
|
||||
|
||||
User API
|
||||
|
||||
NTSTATUS sam_create_user(NT_USER_TOKEN *access, uint32 access_desired, SAM_USER_HANDLE **user)
|
||||
NTSTATUS sam_add_user(SAM_USER_HANDLE *user)
|
||||
NTSTATUS sam_update_user(SAM_USER_HANDLE *user)
|
||||
NTSTATUS sam_delete_user(SAM_USER_HANDLE * user)
|
||||
|
||||
NTSTATUS sam_enum_users(NT_USER_TOKEN *access, DOM_SID *domain, int32 *user_count, SAM_USER_ENUM **users)
|
||||
|
||||
NTSTATUS sam_get_user_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *usersid, SAM_USER_HANDLE **user)
|
||||
NTSTATUS sam_get_user_by_name(NT_USER_TOKEN *access, uint32 access_desired, char *domain, char *name, SAM_USER_HANDLE **user)
|
||||
|
||||
|
||||
Group API
|
||||
|
||||
NTSTATUS sam_create_group(NT_USER_TOKEN *access, uint32 access_desired, uint32 typ, SAM_GROUP_HANDLE **group)
|
||||
NTSTATUS sam_add_group(SAM_GROUP_HANDLE *samgroup)
|
||||
NTSTATUS sam_update_group(SAM_GROUP_HANDLE *samgroup)
|
||||
NTSTATUS sam_delete_group(SAM_GROUP_HANDLE *groupsid)
|
||||
|
||||
NTSTATUS sam_enum_groups(NT_USER_TOKEN *access, DOM_SID *domainsid, uint32 typ, uint32 *groups_count, SAM_GROUP_ENUM **groups)
|
||||
|
||||
NTSTATUS sam_get_group_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *groupsid, SAM_GROUP_HANDLE **group)
|
||||
NTSTATUS sam_get_group_by_name(NT_USER_TOKEN *access, uint32 access_desired, char *domain, char *name, SAM_GROUP_HANDLE **group)
|
||||
|
||||
NTSTATUS sam_add_member_to_group(SAM_GROUP_HANDLE *group, SAM_GROUP_MEMBER *member)
|
||||
NTSTATUS sam_delete_member_from_group(SAM_GROUP_HANDLE *group, SAM_GROUP_MEMBER *member)
|
||||
NTSTATUS sam_enum_groupmembers(SAM_GROUP_HANLDE *group, uint32 *members_count, SAM_GROUP_MEMBER **members)
|
||||
|
||||
NTSTATUS sam_get_groups_of_user(SAM_USER_HANDLE *user, uint32 typ, uint32 *group_count, SAM_GROUP_ENUM **groups)
|
||||
|
||||
|
||||
|
||||
structures
|
||||
|
||||
typedef _SAM_GROUP_MEMBER {
|
||||
DOM_SID sid;
|
||||
BOOL group; /* specifies if it is a group or a user */
|
||||
|
||||
} SAM_GROUP_MEMBER
|
||||
|
||||
typedef struct sam_user_enum {
|
||||
DOM_SID sid;
|
||||
char *username;
|
||||
char *full_name;
|
||||
char *user_desc;
|
||||
uint16 acc_ctrl;
|
||||
} SAM_USER_ENUM;
|
||||
|
||||
typedef struct sam_group_enum {
|
||||
DOM_SID sid;
|
||||
char *groupname;
|
||||
char *comment;
|
||||
} SAM_GROUP_ENUM
|
||||
|
||||
NTSTATUS sam_get_domain_sid(SAM_DOMAIN_HANDLE *domain, DOM_SID **sid)
|
||||
NTSTATUS sam_get_domain_num_users(SAM_DOMAIN_HANDLE *domain, uint32 *num_users)
|
||||
NTSTATUS sam_get_domain_num_groups(SAM_DOMAIN_HANDLE *domain, uint32 *num_groups)
|
||||
NTSTATUS sam_get_domain_num_aliases(SAM_DOMAIN_HANDLE *domain, uint32 *num_aliases)
|
||||
NTSTATUS sam_{get,set}_domain_name(SAM_DOMAIN_HANDLE *domain, char **domain_name)
|
||||
NTSTATUS sam_{get,set}_domain_server(SAM_DOMAIN_HANDLE *domain, char **server_name)
|
||||
NTSTATUS sam_{get,set}_domain_max_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *max_passwordage)
|
||||
NTSTATUS sam_{get,set}_domain_min_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *min_passwordage)
|
||||
NTSTATUS sam_{get,set}_domain_lockout_duration(SAM_DOMAIN_HANDLE *domain, NTTIME *lockout_duration)
|
||||
NTSTATUS sam_{get,set}_domain_reset_count(SAM_DOMAIN_HANDLE *domain, NTTIME *reset_lockout_count)
|
||||
NTSTATUS sam_{get,set}_domain_min_pwdlength(SAM_DOMAIN_HANDLE *domain, uint16 *min_passwordlength)
|
||||
NTSTATUS sam_{get,set}_domain_pwd_history(SAM_DOMAIN_HANDLE *domain, uin16 *password_history)
|
||||
NTSTATUS sam_{get,set}_domain_lockout_count(SAM_DOMAIN_HANDLE *domain, uint16 *lockout_count)
|
||||
NTSTATUS sam_{get,set}_domain_force_logoff(SAM_DOMAIN_HANDLE *domain, BOOL *force_logoff)
|
||||
NTSTATUS sam_{get,set}_domain_login_pwdchange(SAM_DOMAIN_HANDLE *domain, BOOL *login_pwdchange)
|
||||
|
||||
NTSTATUS sam_get_user_sid(SAM_USER_HANDLE *user, DOM_SID **sid)
|
||||
NTSTATUS sam_{get,set}_user_pgroup(SAM_USER_HANDLE *user, DOM_SID **pgroup)
|
||||
NTSTATUS sam_{get,set}_user_name(SAM_USER_HANDLE *user, char **username)
|
||||
NTSTATUS sam_{get,set}_user_fullname(SAM_USER_HANDLE *user, char** fullname)
|
||||
NTSTATUS sam_{get,set}_user_description(SAM_USER_HANDLE *user, char **description)
|
||||
NTSTATUS sam_{get,set}_user_home_dir(SAM_USER_HANDLE *user, char **home_dir)
|
||||
NTSTATUS sam_{get,set}_user_dir_drive(SAM_USER_HANDLE *user, char **dir_drive)
|
||||
NTSTATUS sam_{get,set}_user_logon_script(SAM_USER_HANDLE *user, char **logon_script)
|
||||
NTSTATUS sam_{get,set}_user_profile_path(SAM_USER_HANDLE *user, char **profile_path)
|
||||
NTSTATUS sam_{get,set}_user_workstations(SAM_USER_HANDLE *user, char **workstations)
|
||||
NTSTATUS sam_{get,set}_user_munged_dial(SAM_USER_HANDLE *user, char **munged_dial)
|
||||
NTSTATUS sam_{get,set}_user_lm_pwd(SAM_USER_HANDLE *user, DATA_BLOB *lm_pwd)
|
||||
NTSTATUS sam_{get,set}_user_nt_pwd(SAM_USER_HANDLE *user, DATA_BLOB *nt_pwd)
|
||||
NTSTATUS sam_{get,set}_user_plain_pwd(SAM_USER_HANDLE *user, DATA_BLOB *plaintext_pwd)
|
||||
NTSTATUS sam_{get,set}_user_acct_ctrl(SAM_USER_HANDLE *user, uint16 *acct_ctrl)
|
||||
NTSTATUS sam_{get,set}_user_logon_divs(SAM_USER_HANDLE *user, uint16 *logon_divs)
|
||||
NTSTATUS sam_{get,set}_user_hours(SAM_USER_HANDLE *user, uint32 *hours_len, uint8 **hours)
|
||||
NTSTATUS sam_{get,set}_user_logon_time(SAM_USER_HANDLE *user, NTTIME *logon_time)
|
||||
NTSTATUS sam_{get,set}_user_logoff_time(SAM_USER_HANDLE *user, NTTIME *logoff_time)
|
||||
NTSTATUS sam_{get,set}_user_kickoff_time(SAM_USER_HANDLE *user, NTTIME kickoff_time)
|
||||
NTSTATUS sam_{get,set}_user_pwd_last_set(SAM_USER_HANDLE *user, NTTIME pwd_last_set)
|
||||
NTSTATUS sam_{get,set}_user_pwd_can_change(SAM_USER_HANDLE *user, NTTIME pwd_can_change)
|
||||
NTSTATUS sam_{get,set}_user_pwd_must_change(SAM_USER_HANDLE *user, NTTIME pwd_must_change)
|
||||
NTSTATUS sam_{get,set}_user_unknown_1(SAM_USER_HANDLE *user, char **unknown_1)
|
||||
NTSTATUS sam_{get,set}_user_unknown_2(SAM_USER_HANDLE *user, uint32 *unknown_2)
|
||||
NTSTATUS sam_{get,set}_user_unknown_3(SAM_USER_HANDLE *user, uint32 *unknown_3)
|
||||
NTSTATUS sam_{get,set}_user_unknown_4(SAM_USER_HANDLE *user, uint32 *unknown_4)
|
||||
|
||||
NTSTATUS sam_get_group_sid(SAM_GROUP_HANDLE *group, DOM_SID **sid)
|
||||
NTSTATUS sam_get_group_typ(SAM_GROUP_HANDLE *group, uint32 *typ)
|
||||
NTSTATUS sam_{get,set}_group_name(SAM_GROUP_HANDLE *group, char **group_name)
|
||||
NTSTATUS sam_{get,set}_group_comment(SAM_GROUP_HANDLE *group, char **comment)
|
||||
NTSTATUS sam_{get,set}_group_priv_set(SAM_GROUP_HANDLE *group, PRIVILEGE_SET *priv_set)
|
102
source3/script/cvslog.pl
Executable file
102
source3/script/cvslog.pl
Executable file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
my ( $tag, $filename, $date );
|
||||
my ( $tmp, $change_flag );
|
||||
|
||||
if ( $#ARGV != 2 ) {
|
||||
|
||||
print "Usage: ", $0, " cvstag date file\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
$tag = $ARGV[0];
|
||||
$date = $ARGV[1];
|
||||
$filename = $ARGV[2];
|
||||
|
||||
print STDERR "$filename\n";
|
||||
|
||||
open ( CVSLOG, "cvs log -d\"$date\" $filename |" ) || die $!;
|
||||
|
||||
##
|
||||
## First get the branch revision number
|
||||
##
|
||||
undef $revision;
|
||||
while ( !defined($revision) ) {
|
||||
if ( eof( \*CVSLOG ) ) {
|
||||
print STDERR "Premature end of cvs log output!\n";
|
||||
exit (1);
|
||||
}
|
||||
|
||||
$string = <CVSLOG>;
|
||||
chomp( $string );
|
||||
|
||||
if ( $string =~ /$tag:/ ) {
|
||||
( $tmp, $revision ) = split( /:/, $string );
|
||||
$revision =~ s/\s+//g;
|
||||
$revision =~ s/\.0\./\./g;
|
||||
}
|
||||
}
|
||||
|
||||
##
|
||||
## Setup the beginning of the first record
|
||||
##
|
||||
$string = "";
|
||||
while ( $string !~ /^-+/ ) {
|
||||
$string = <CVSLOG>;
|
||||
exit(0) if ( eof(\*CVSLOG) );
|
||||
}
|
||||
|
||||
##
|
||||
## Loop starting at the revision number for the entry
|
||||
##
|
||||
|
||||
while ( $string = <CVSLOG> ) {
|
||||
|
||||
($tmp, $entry_rev) = split( /\s+/, $string );
|
||||
if ( equal_revision( $revision, $entry_rev ) ) {
|
||||
if ( ! defined($change_flag) ) {
|
||||
print "++++++++++++++++++++++++++++++++++++++++++++++++++\n";
|
||||
print "## $filename\n";
|
||||
print "++\n";
|
||||
$change_flag = 1;
|
||||
}
|
||||
|
||||
while ( $string !~ /^-+/ && !eof(CVSLOG) ) {
|
||||
print "$string";
|
||||
$string = <CVSLOG>;
|
||||
}
|
||||
}
|
||||
else {
|
||||
while ( ($string !~ /^-+/) && !eof(CVSLOG) ) {
|
||||
$string = <CVSLOG>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close( CVSLOG );
|
||||
exit 0;
|
||||
|
||||
##############################################################
|
||||
##
|
||||
sub equal_revision {
|
||||
my ( $branch, $newfile ) = @_;
|
||||
my ( $indx );
|
||||
my ( @branch_rev, @file_rev );
|
||||
|
||||
@branch_rev = split( /\./, $branch );
|
||||
@file_rev = split( /\./, $newfile );
|
||||
|
||||
return 0 if ( $#branch_rev != ($#file_rev - 1) );
|
||||
|
||||
$indx = 0;
|
||||
while( $indx <= $#branch_rev ) {
|
||||
if ( $branch_rev[$indx] != $file_rev[$indx] ) {
|
||||
return 0;
|
||||
}
|
||||
$indx++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user