s4 upgradeprovision: Add a function for schema reloading
[mat/samba.git] / source4 / scripting / bin / testparm
1 #!/usr/bin/env python
2 # vim: expandtab ft=python
3 #
4 #   Unix SMB/CIFS implementation.
5 #   Test validity of smb.conf
6 #   Copyright (C) Karl Auer 1993, 1994-1998
7 #
8 #   Extensively modified by Andrew Tridgell, 1995
9 #   Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002
10 #   Updated for Samba4 by Andrew Bartlett <abartlet@samba.org> 2006
11 #   Converted to Python by Jelmer Vernooij <jelmer@samba.org> 2010
12 #   
13 #   This program is free software; you can redistribute it and/or modify
14 #   it under the terms of the GNU General Public License as published by
15 #   the Free Software Foundation; either version 3 of the License, or
16 #   (at your option) any later version.
17 #   
18 #   This program is distributed in the hope that it will be useful,
19 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
20 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 #   GNU General Public License for more details.
22 #   
23 #   You should have received a copy of the GNU General Public License
24 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
25 #
26 # Testbed for loadparm.c/params.c
27 #
28 # This module simply loads a specified configuration file and
29 # if successful, dumps it's contents to stdout. Note that the
30 # operation is performed with DEBUGLEVEL at 3.
31 #
32 # Useful for a quick 'syntax check' of a configuration file.
33 #
34
35 import logging
36 import optparse
37 import os
38 import sys
39
40 # Find right directory when running from source tree
41 sys.path.insert(0, "bin/python")
42
43 import samba
44 from samba import getopt as options
45
46 # Here we do a set of 'hard coded' checks for bad
47 # configuration settings.
48
49 def do_global_checks(lp, logger):
50     valid = True
51
52     netbios_name = lp.get("netbios name")
53     if not samba.valid_netbios_name(netbios_name):
54         logger.error("netbios name %s is not a valid netbios name", 
55                      netbios_name)
56         valid = False
57
58     workgroup = lp.get("workgroup")
59     if not samba.valid_netbios_name(workgroup):
60         logger.error("workgroup name %s is not a valid netbios name", 
61                      workgroup)
62         valid = False
63
64     lockdir = lp.get("lockdir")
65
66     if not os.path.isdir(lockdir):
67         logger.error("lock directory %s does not exist", lockdir)
68         valid = False
69
70     piddir = lp.get("pid directory")
71
72     if not os.path.isdir(piddir):
73         logger.error("pid directory %s does not exist", piddir)
74         valid = False
75
76     winbind_separator = lp.get("winbind separator")
77
78     if len(winbind_separator) != 1:
79         logger.error("the 'winbind separator' parameter must be a single "
80                      "character.")
81         valid = False
82
83     if winbind_separator == '+':
84         logger.error("'winbind separator = +' might cause problems with group "
85                      "membership.")
86         valid = False
87
88     return valid
89
90
91 def allow_access(deny_list, allow_list, cname, caddr):
92     raise NotImplementedError(allow_access)
93
94
95 def do_share_checks(lp, logger):
96     valid = True
97     for s in lp.services():
98         if len(s) > 12:
99             logger.warning("You have some share names that are longer than 12 "
100                 "characters. These may not be accessible to some older "
101                 "clients. (Eg. Windows9x, WindowsMe, and not listed in "
102                 "smbclient in Samba 3.0.)")
103             break
104
105     for s in lp.services():
106         deny_list = lp.get("hosts deny", s)
107         allow_list = lp.get("hosts allow", s)
108         if deny_list:
109             for entry in deny_list:
110                 if "*" in entry or "?" in entry:
111                     logger.error("Invalid character (* or ?) in hosts deny "
112                                  "list (%s) for service %s.", entry, s)
113                     valid = False
114
115         if allow_list:
116             for entry in allow_list:
117                 if "*" in entry or "?" in entry:
118                     logger.error("Invalid character (* or ?) in hosts allow "
119                                  "list (%s) for service %s.", entry, s)
120                     valid = False
121     return valid
122
123 def check_client_access(lp, cname, caddr):
124     # this is totally ugly, a real `quick' hack
125     for s in lp.services():
126         if (allow_access(lp.get("hosts deny"), lp.get("hosts allow"), cname,
127                          caddr) and
128             allow_access(lp.get("hosts deny", s), lp.get("hosts allow", s),
129                          cname, caddr)):
130             logger.info("Allow connection from %s (%s) to %s", cname, caddr, s)
131         else:
132             logger.info("Deny connection from %s (%s) to %s", cname, caddr, s)
133
134
135 if __name__ == '__main__':
136     parser = optparse.OptionParser("testparm [OPTION...] [host-name] [host-ip]")
137     parser.add_option("--section-name", type="string", metavar="SECTION",
138             help="Limit testparm to a named section")
139     parser.add_option("--parameter-name", type="string", metavar="PARAMETER",
140             help="Limit testparm to a named parameter")
141     parser.add_option("--client-name", type="string", metavar="HOSTNAME",
142             help="Client DNS name for 'hosts allow' checking "
143                  "(should match reverse lookup)")
144     parser.add_option("--client-ip", type="string", metavar="IP",
145             help="Client IP address for 'hosts allow' checking")
146     parser.add_option("--suppress-prompt", action="store_true", default=False,
147             help="Suppress prompt for enter")
148     parser.add_option("-v", "--verbose", action="store_true", 
149             default=False, help="Show default options too")
150     parser.add_option_group(options.VersionOptions(parser))
151     # We need support for smb.conf macros before this will work again 
152     parser.add_option("--server", type="string",
153             help="Set %%L macro to servername")
154     # These are harder to do with the new code structure
155     parser.add_option("--show-all-parameters", action="store_true",
156             default=False, help="Show the parameters, type, possible values")
157
158     sambaopts = options.SambaOptions(parser)
159     parser.add_option_group(sambaopts)
160
161     opts, args = parser.parse_args()
162
163
164 #    if (show_all_parameters) {
165 #        show_parameter_list()
166 #        exit(0)
167 #    }
168
169     if len(args) > 0:
170         cname = args[0]
171     else:
172         cname = None
173     if len(args) > 1:
174         caddr = args[1]
175     else:
176         caddr = None
177
178     if cname is not None and caddr is None:
179         print "Both a DNS name and an IP address are required for the host " \
180               "access check."
181         sys.exit(1)
182
183 #   FIXME: We need support for smb.conf macros before this will work again 
184 #
185 #    if (new_local_machine) {
186 #        set_local_machine_name(new_local_machine, True)
187 #    }
188
189     lp = sambaopts.get_loadparm()
190
191     # We need this to force the output
192     samba.set_debug_level(2)
193
194     logger = logging.getLogger("testparm")
195     logger.addHandler(logging.StreamHandler(sys.stdout))
196
197     logger.info("Loaded smb config files from %s", lp.configfile)
198     logger.info("Loaded services file OK.")
199
200     valid = do_global_checks(lp, logger)
201     valid = valid and do_share_checks(lp, logger)
202     if cname is not None:
203         check_client_access(lp, cname, caddr)
204     else:
205         if opts.section_name is not None or opts.parameter_name is not None:
206             if opts.parameter_name is None:
207                 lp[opts.section_name].dump(sys.stdout, lp.default_service,
208                                            opts.verbose)
209             else:
210                 print lp.get(opts.parameter_name, opts.section_name)
211         else:
212             if not opts.suppress_prompt:
213                 print "Press enter to see a dump of your service definitions\n"
214                 sys.stdin.readline()
215             lp.dump(sys.stdout, opts.verbose)
216     if valid:
217         sys.exit(0)
218     else:
219         sys.exit(1)