2 Unix SMB/CIFS implementation.
3 Test validity of smb.conf
4 Copyright (C) Karl Auer 1993, 1994-1998
6 Extensively modified by Andrew Tridgell, 1995
7 Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * Testbed for loadparm.c/params.c
26 * This module simply loads a specified configuration file and
27 * if successful, dumps it's contents to stdout. Note that the
28 * operation is performed with DEBUGLEVEL at 3.
30 * Useful for a quick 'syntax check' of a configuration file.
35 #include "system/filesys.h"
36 #include "popt_common.h"
37 #include "lib/param/loadparm.h"
41 /*******************************************************************
42 Check if a directory exists.
43 ********************************************************************/
45 static bool directory_exist_stat(const char *dname,SMB_STRUCT_STAT *st)
53 if (sys_stat(dname, st, false) != 0)
56 ret = S_ISDIR(st->st_ex_mode);
63 const char *domain_name;
69 struct idmap_domains {
70 struct idmap_config *c;
75 static bool lp_scan_idmap_found_domain(const char *string,
81 if (matches[1].rm_so == -1) {
82 fprintf(stderr, "Found match, but no name - invalid idmap config");
85 if (matches[1].rm_eo <= matches[1].rm_so) {
86 fprintf(stderr, "Invalid match - invalid idmap config");
91 struct idmap_domains *d = private_data;
92 struct idmap_config *c = &d->c[d->count];
93 regoff_t len = matches[1].rm_eo - matches[1].rm_so;
94 char domname[len + 1];
96 if (d->count >= d->size) {
100 memcpy(domname, string + matches[1].rm_so, len);
103 c->domain_name = talloc_strdup_upper(d->c, domname);
104 if (c->domain_name == NULL) {
107 c->backend = talloc_strdup(d->c, lp_idmap_backend(domname));
108 if (c->backend == NULL) {
112 ok = lp_idmap_range(domname, &c->low, &c->high);
115 "ERROR: Invalid idmap range for domain "
124 return false; /* Keep scanning */
127 static bool do_idmap_check(void)
129 struct idmap_domains *d;
134 d = talloc_zero(talloc_tos(), struct idmap_domains);
141 d->c = talloc_array(d, struct idmap_config, d->size);
146 rc = lp_wi_scan_global_parametrics("idmapconfig\\(.*\\):backend",
148 lp_scan_idmap_found_domain,
152 "FATAL: wi_scan_global_parametrics failed: %d",
156 for (i = 0; i < d->count; i++) {
157 struct idmap_config *c = &d->c[i];
160 for (j = 0; j < d->count && j != i; j++) {
161 struct idmap_config *x = &d->c[j];
163 if ((c->low >= x->low && c->low <= x->high) ||
164 (c->high >= x->low && c->high <= x->high)) {
165 /* Allow overlapping ranges for idmap_ad */
166 ok = strequal(c->backend, x->backend);
168 ok = strequal(c->backend, "ad");
171 "NOTE: The idmap_ad "
172 "range for the domain "
173 "%s overlaps with the "
182 "ERROR: The idmap range for the domain "
183 "%s (%s) overlaps with the range of "
201 /***********************************************
202 Here we do a set of 'hard coded' checks for bad
203 configuration settings.
204 ************************************************/
206 static int do_global_checks(void)
210 const char *socket_options;
212 if (lp_security() >= SEC_DOMAIN && !lp_encrypt_passwords()) {
213 fprintf(stderr, "ERROR: in 'security=domain' mode the "
214 "'encrypt passwords' parameter must always be "
215 "set to 'true'.\n\n");
219 if (lp_we_are_a_wins_server() && lp_wins_server_list()) {
220 fprintf(stderr, "ERROR: both 'wins support = true' and "
221 "'wins server = <server list>' cannot be set in "
222 "the smb.conf file. nmbd will abort with this "
227 if (strequal(lp_workgroup(), lp_netbios_name())) {
228 fprintf(stderr, "WARNING: 'workgroup' and 'netbios name' "
232 if (strlen(lp_netbios_name()) > 15) {
233 fprintf(stderr, "WARNING: The 'netbios name' is too long "
234 "(max. 15 chars).\n\n");
237 if (!directory_exist_stat(lp_lock_directory(), &st)) {
238 fprintf(stderr, "ERROR: lock directory %s does not exist\n\n",
239 lp_lock_directory());
241 } else if ((st.st_ex_mode & 0777) != 0755) {
242 fprintf(stderr, "WARNING: lock directory %s should have "
243 "permissions 0755 for browsing to work\n\n",
244 lp_lock_directory());
247 if (!directory_exist_stat(lp_state_directory(), &st)) {
248 fprintf(stderr, "ERROR: state directory %s does not exist\n\n",
249 lp_state_directory());
251 } else if ((st.st_ex_mode & 0777) != 0755) {
252 fprintf(stderr, "WARNING: state directory %s should have "
253 "permissions 0755 for browsing to work\n\n",
254 lp_state_directory());
257 if (!directory_exist_stat(lp_cache_directory(), &st)) {
258 fprintf(stderr, "ERROR: cache directory %s does not exist\n\n",
259 lp_cache_directory());
261 } else if ((st.st_ex_mode & 0777) != 0755) {
262 fprintf(stderr, "WARNING: cache directory %s should have "
263 "permissions 0755 for browsing to work\n\n",
264 lp_cache_directory());
267 if (!directory_exist_stat(lp_pid_directory(), &st)) {
268 fprintf(stderr, "ERROR: pid directory %s does not exist\n\n",
273 if (lp_passdb_expand_explicit()) {
274 fprintf(stderr, "WARNING: passdb expand explicit = yes is "
281 socket_options = lp_socket_options();
282 if (socket_options != NULL &&
283 (strstr(socket_options, "SO_SNDBUF") ||
284 strstr(socket_options, "SO_RCVBUF") ||
285 strstr(socket_options, "SO_SNDLOWAT") ||
286 strstr(socket_options, "SO_RCVLOWAT")))
289 "WARNING: socket options = %s\n"
290 "This warning is printed because you set one of the\n"
291 "following options: SO_SNDBUF, SO_RCVBUF, SO_SNDLOWAT,\n"
293 "Modern server operating systems are tuned for\n"
294 "high network performance in the majority of situations;\n"
295 "when you set 'socket options' you are overriding those\n"
297 "Linux in particular has an auto-tuning mechanism for\n"
298 "buffer sizes (SO_SNDBUF, SO_RCVBUF) that will be\n"
299 "disabled if you specify a socket buffer size. This can\n"
300 "potentially cripple your TCP/IP stack.\n\n"
301 "Getting the 'socket options' correct can make a big\n"
302 "difference to your performance, but getting them wrong\n"
303 "can degrade it by just as much. As with any other low\n"
304 "level setting, if you must make changes to it, make\n "
305 "small changes and test the effect before making any\n"
306 "large changes.\n\n",
311 * Password server sanity checks.
314 if((lp_security() >= SEC_DOMAIN) && !*lp_password_server()) {
315 const char *sec_setting;
316 if(lp_security() == SEC_DOMAIN)
317 sec_setting = "domain";
318 else if(lp_security() == SEC_ADS)
323 fprintf(stderr, "ERROR: The setting 'security=%s' requires the "
324 "'password server' parameter be set to the "
325 "default value * or a valid password server.\n\n",
330 if((lp_security() >= SEC_DOMAIN) && (strcmp(lp_password_server(), "*") != 0)) {
331 const char *sec_setting;
332 if(lp_security() == SEC_DOMAIN)
333 sec_setting = "domain";
334 else if(lp_security() == SEC_ADS)
339 fprintf(stderr, "WARNING: The setting 'security=%s' should NOT "
340 "be combined with the 'password server' "
342 "(by default Samba will discover the correct DC "
343 "to contact automatically).\n\n",
348 * Password chat sanity checks.
351 if(lp_security() == SEC_USER && lp_unix_password_sync()) {
354 * Check that we have a valid lp_passwd_program() if not using pam.
358 if (!lp_pam_password_change()) {
361 if((lp_passwd_program(talloc_tos()) == NULL) ||
362 (strlen(lp_passwd_program(talloc_tos())) == 0))
365 "ERROR: the 'unix password sync' "
366 "parameter is set and there is no valid "
367 "'passwd program' parameter.\n\n");
370 const char *passwd_prog;
371 char *truncated_prog = NULL;
374 passwd_prog = lp_passwd_program(talloc_tos());
376 next_token_talloc(talloc_tos(),
378 &truncated_prog, NULL);
379 if (truncated_prog && access(truncated_prog, F_OK) == -1) {
381 "ERROR: the 'unix password sync' "
382 "parameter is set and the "
383 "'passwd program' (%s) cannot be "
384 "executed (error was %s).\n\n",
395 if(lp_passwd_chat(talloc_tos()) == NULL) {
397 "ERROR: the 'unix password sync' parameter is "
398 "set and there is no valid 'passwd chat' "
403 if ((lp_passwd_program(talloc_tos()) != NULL) &&
404 (strlen(lp_passwd_program(talloc_tos())) > 0))
406 /* check if there's a %u parameter present */
407 if(strstr_m(lp_passwd_program(talloc_tos()), "%u") == NULL) {
409 "ERROR: the 'passwd program' (%s) "
410 "requires a '%%u' parameter.\n\n",
411 lp_passwd_program(talloc_tos()));
417 * Check that we have a valid script and that it hasn't
418 * been written to expect the old password.
421 if(lp_encrypt_passwords()) {
422 if(strstr_m( lp_passwd_chat(talloc_tos()), "%o")!=NULL) {
424 "ERROR: the 'passwd chat' script [%s] "
425 "expects to use the old plaintext "
426 "password via the %%o substitution. With "
427 "encrypted passwords this is not "
429 lp_passwd_chat(talloc_tos()) );
435 if (strlen(lp_winbind_separator()) != 1) {
436 fprintf(stderr, "ERROR: the 'winbind separator' parameter must "
437 "be a single character.\n\n");
441 if (*lp_winbind_separator() == '+') {
442 fprintf(stderr, "'winbind separator = +' might cause problems "
443 "with group membership.\n\n");
446 if (lp_algorithmic_rid_base() < BASE_RID) {
447 /* Try to prevent admin foot-shooting, we can't put algorithmic
448 rids below 1000, that's the 'well known RIDs' on NT */
449 fprintf(stderr, "'algorithmic rid base' must be equal to or "
450 "above %lu\n\n", BASE_RID);
453 if (lp_algorithmic_rid_base() & 1) {
454 fprintf(stderr, "'algorithmic rid base' must be even.\n\n");
457 if (lp_server_role() != ROLE_STANDALONE) {
458 const char *default_backends[] = {
459 "tdb", "tdb2", "ldap", "autorid", "hash"
461 const char *idmap_backend;
462 bool valid_backend = false;
466 idmap_backend = lp_idmap_default_backend();
468 for (i = 0; i < ARRAY_SIZE(default_backends); i++) {
469 ok = strequal(idmap_backend, default_backends[i]);
471 valid_backend = true;
475 if (!valid_backend) {
477 fprintf(stderr, "ERROR: Do not use the '%s' backend "
478 "as the default idmap backend!\n\n",
482 ok = do_idmap_check();
489 if (lp_preload_modules()) {
490 fprintf(stderr, "WARNING: 'preload modules = ' set while loading "
491 "plugins not supported.\n\n");
495 if (!lp_passdb_backend()) {
496 fprintf(stderr, "ERROR: passdb backend must have a value or be "
500 if (lp_os_level() > 255) {
501 fprintf(stderr, "WARNING: Maximum value for 'os level' is "
505 if (strequal(lp_dos_charset(), "UTF8") || strequal(lp_dos_charset(), "UTF-8")) {
506 fprintf(stderr, "ERROR: 'dos charset' must not be UTF8\n\n");
514 * per-share logic tests
516 static void do_per_share_checks(int s)
518 const char **deny_list = lp_hosts_deny(s);
519 const char **allow_list = lp_hosts_allow(s);
520 const char **vfs_objects = NULL;
522 static bool uses_fruit;
523 static bool doesnt_use_fruit;
524 static bool fruit_mix_warned;
527 for (i=0; deny_list[i]; i++) {
528 char *hasstar = strchr_m(deny_list[i], '*');
529 char *hasquery = strchr_m(deny_list[i], '?');
530 if(hasstar || hasquery) {
532 "Invalid character %c in hosts deny list "
533 "(%s) for service %s.\n\n",
534 hasstar ? *hasstar : *hasquery,
536 lp_servicename(talloc_tos(), s));
542 for (i=0; allow_list[i]; i++) {
543 char *hasstar = strchr_m(allow_list[i], '*');
544 char *hasquery = strchr_m(allow_list[i], '?');
545 if(hasstar || hasquery) {
547 "Invalid character %c in hosts allow "
548 "list (%s) for service %s.\n\n",
549 hasstar ? *hasstar : *hasquery,
551 lp_servicename(talloc_tos(), s));
556 if(lp_level2_oplocks(s) && !lp_oplocks(s)) {
557 fprintf(stderr, "Invalid combination of parameters for service "
558 "%s. Level II oplocks can only be set if oplocks "
560 lp_servicename(talloc_tos(), s));
563 if (!lp_store_dos_attributes(s) && lp_map_hidden(s)
564 && !(lp_create_mask(s) & S_IXOTH))
567 "Invalid combination of parameters for service %s. Map "
568 "hidden can only work if create mask includes octal "
570 lp_servicename(talloc_tos(), s));
572 if (!lp_store_dos_attributes(s) && lp_map_hidden(s)
573 && (lp_force_create_mode(s) & S_IXOTH))
576 "Invalid combination of parameters for service "
577 "%s. Map hidden can only work if force create mode "
578 "excludes octal 01 (S_IXOTH).\n\n",
579 lp_servicename(talloc_tos(), s));
581 if (!lp_store_dos_attributes(s) && lp_map_system(s)
582 && !(lp_create_mask(s) & S_IXGRP))
585 "Invalid combination of parameters for service "
586 "%s. Map system can only work if create mask includes "
587 "octal 010 (S_IXGRP).\n\n",
588 lp_servicename(talloc_tos(), s));
590 if (!lp_store_dos_attributes(s) && lp_map_system(s)
591 && (lp_force_create_mode(s) & S_IXGRP))
594 "Invalid combination of parameters for service "
595 "%s. Map system can only work if force create mode "
596 "excludes octal 010 (S_IXGRP).\n\n",
597 lp_servicename(talloc_tos(), s));
599 if (lp_printing(s) == PRINT_CUPS && *(lp_print_command(talloc_tos(), s)) != '\0') {
601 "Warning: Service %s defines a print command, but "
602 "parameter is ignored when using CUPS libraries.\n\n",
603 lp_servicename(talloc_tos(), s));
606 vfs_objects = lp_vfs_objects(s);
607 if (vfs_objects && str_list_check(vfs_objects, "fruit")) {
610 doesnt_use_fruit = true;
613 if (uses_fruit && doesnt_use_fruit && !fruit_mix_warned) {
614 fruit_mix_warned = true;
616 "WARNING: some services use vfs_fruit, others don't. Mounting them "
617 "in conjunction on OS X clients results in undefined behaviour.\n\n");
621 int main(int argc, const char *argv[])
623 const char *config_file = get_dyn_CONFIGFILE();
625 static int silent_mode = False;
626 static int show_all_parameters = False;
629 static char *parameter_name = NULL;
630 static const char *section_name = NULL;
633 static int show_defaults;
634 static int skip_logic_checks = 0;
636 struct poptOption long_options[] = {
638 {"suppress-prompt", 's', POPT_ARG_VAL, &silent_mode, 1, "Suppress prompt for enter"},
639 {"verbose", 'v', POPT_ARG_NONE, &show_defaults, 1, "Show default options too"},
640 {"skip-logic-checks", 'l', POPT_ARG_NONE, &skip_logic_checks, 1, "Skip the global checks"},
641 {"show-all-parameters", '\0', POPT_ARG_VAL, &show_all_parameters, True, "Show the parameters, type, possible values" },
642 {"parameter-name", '\0', POPT_ARG_STRING, ¶meter_name, 0, "Limit testparm to a named parameter" },
643 {"section-name", '\0', POPT_ARG_STRING, §ion_name, 0, "Limit testparm to a named section" },
645 POPT_COMMON_DEBUGLEVEL
650 TALLOC_CTX *frame = talloc_stackframe();
654 * Set the default debug level to 2.
655 * Allow it to be overridden by the command line,
658 lp_set_cmdline("log level", "2");
660 pc = poptGetContext(NULL, argc, argv, long_options,
661 POPT_CONTEXT_KEEP_FIRST);
662 poptSetOtherOptionHelp(pc, "[OPTION...] <config-file> [host-name] [host-ip]");
664 while(poptGetNextOpt(pc) != -1);
666 if (show_all_parameters) {
667 show_parameter_list();
671 setup_logging(poptGetArg(pc), DEBUG_STDERR);
674 config_file = poptGetArg(pc);
676 cname = poptGetArg(pc);
677 caddr = poptGetArg(pc);
681 if ( cname && ! caddr ) {
682 printf ( "ERROR: You must specify both a machine name and an IP address.\n" );
687 fprintf(stderr,"Load smb config files from %s\n",config_file);
689 if (!lp_load_with_registry_shares(config_file)) {
690 fprintf(stderr,"Error loading services.\n");
695 fprintf(stderr,"Loaded services file OK.\n");
697 if (skip_logic_checks == 0) {
698 ret = do_global_checks();
701 for (s=0;s<1000;s++) {
703 if (strlen(lp_servicename(talloc_tos(), s)) > 12) {
704 fprintf(stderr, "WARNING: You have some share names that are longer than 12 characters.\n" );
705 fprintf(stderr, "These may not be accessible to some older clients.\n" );
706 fprintf(stderr, "(Eg. Windows9x, WindowsMe, and smbclient prior to Samba 3.0.)\n" );
711 for (s=0;s<1000;s++) {
712 if (VALID_SNUM(s) && (skip_logic_checks == 0)) {
713 do_per_share_checks(s);
718 if (!section_name && !parameter_name) {
720 "Server role: %s\n\n",
721 server_role_str(lp_server_role()));
726 fprintf(stderr,"Press enter to see a dump of your service definitions\n");
730 if (parameter_name || section_name) {
731 bool isGlobal = False;
732 s = GLOBAL_SECTION_SNUM;
735 section_name = GLOBAL_NAME;
737 } else if ((isGlobal=!strwicmp(section_name, GLOBAL_NAME)) == 0 &&
738 (s=lp_servicenumber(section_name)) == -1) {
739 fprintf(stderr,"Unknown section %s\n",
744 if (parameter_name) {
745 if (!dump_a_parameter( s, parameter_name, stdout, isGlobal)) {
746 fprintf(stderr,"Parameter %s unknown for section %s\n",
747 parameter_name, section_name);
752 if (isGlobal == True)
753 lp_dump(stdout, show_defaults, 0);
755 lp_dump_one(stdout, show_defaults, s);
760 lp_dump(stdout, show_defaults, lp_numservices());
764 /* this is totally ugly, a real `quick' hack */
765 for (s=0;s<1000;s++) {
767 if (allow_access(lp_hosts_deny(-1), lp_hosts_allow(-1), cname, caddr)
768 && allow_access(lp_hosts_deny(s), lp_hosts_allow(s), cname, caddr)) {
769 fprintf(stderr,"Allow connection from %s (%s) to %s\n",
770 cname,caddr,lp_servicename(talloc_tos(), s));
772 fprintf(stderr,"Deny connection from %s (%s) to %s\n",
773 cname,caddr,lp_servicename(talloc_tos(), s));