testparm: don't warn for TCP_NODELAY in socket options.
[amitay/samba.git] / source3 / utils / testparm.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Test validity of smb.conf
4    Copyright (C) Karl Auer 1993, 1994-1998
5
6    Extensively modified by Andrew Tridgell, 1995
7    Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002
8    
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.
13    
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.
18    
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/>.
21 */
22
23 /*
24  * Testbed for loadparm.c/params.c
25  *
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.
29  *
30  * Useful for a quick 'syntax check' of a configuration file.
31  *
32  */
33
34 #include "includes.h"
35 #include "system/filesys.h"
36 #include "popt_common.h"
37 #include "lib/param/loadparm.h"
38
39 /*******************************************************************
40  Check if a directory exists.
41 ********************************************************************/
42
43 static bool directory_exist_stat(const char *dname,SMB_STRUCT_STAT *st)
44 {
45         SMB_STRUCT_STAT st2;
46         bool ret;
47
48         if (!st)
49                 st = &st2;
50
51         if (sys_stat(dname, st, false) != 0)
52                 return(False);
53
54         ret = S_ISDIR(st->st_ex_mode);
55         if(!ret)
56                 errno = ENOTDIR;
57         return ret;
58 }
59
60 /***********************************************
61  Here we do a set of 'hard coded' checks for bad
62  configuration settings.
63 ************************************************/
64
65 static int do_global_checks(void)
66 {
67         int ret = 0;
68         SMB_STRUCT_STAT st;
69         const char *socket_options;
70
71         if (lp_security() >= SEC_DOMAIN && !lp_encrypted_passwords()) {
72                 fprintf(stderr, "ERROR: in 'security=domain' mode the "
73                                 "'encrypt passwords' parameter must always be "
74                                 "set to 'true'.\n\n");
75                 ret = 1;
76         }
77
78         if (lp_we_are_a_wins_server() && lp_wins_server_list()) {
79                 fprintf(stderr, "ERROR: both 'wins support = true' and "
80                                 "'wins server = <server list>' cannot be set in "
81                                 "the smb.conf file. nmbd will abort with this "
82                                 "setting.\n\n");
83                 ret = 1;
84         }
85
86         if (strequal(lp_workgroup(), lp_netbios_name())) {
87                 fprintf(stderr, "WARNING: 'workgroup' and 'netbios name' "
88                                 "must differ.\n\n");
89         }
90
91         if (!directory_exist_stat(lp_lockdir(), &st)) {
92                 fprintf(stderr, "ERROR: lock directory %s does not exist\n\n",
93                        lp_lockdir());
94                 ret = 1;
95         } else if ((st.st_ex_mode & 0777) != 0755) {
96                 fprintf(stderr, "WARNING: lock directory %s should have "
97                                 "permissions 0755 for browsing to work\n\n",
98                        lp_lockdir());
99         }
100
101         if (!directory_exist_stat(lp_statedir(), &st)) {
102                 fprintf(stderr, "ERROR: state directory %s does not exist\n\n",
103                        lp_statedir());
104                 ret = 1;
105         } else if ((st.st_ex_mode & 0777) != 0755) {
106                 fprintf(stderr, "WARNING: state directory %s should have "
107                                 "permissions 0755 for browsing to work\n\n",
108                        lp_statedir());
109         }
110
111         if (!directory_exist_stat(lp_cachedir(), &st)) {
112                 fprintf(stderr, "ERROR: cache directory %s does not exist\n\n",
113                        lp_cachedir());
114                 ret = 1;
115         } else if ((st.st_ex_mode & 0777) != 0755) {
116                 fprintf(stderr, "WARNING: cache directory %s should have "
117                                 "permissions 0755 for browsing to work\n\n",
118                        lp_cachedir());
119         }
120
121         if (!directory_exist_stat(lp_piddir(), &st)) {
122                 fprintf(stderr, "ERROR: pid directory %s does not exist\n\n",
123                        lp_piddir());
124                 ret = 1;
125         }
126
127         if (lp_passdb_expand_explicit()) {
128                 fprintf(stderr, "WARNING: passdb expand explicit = yes is "
129                                 "deprecated\n\n");
130         }
131
132         /*
133          * Socket options.
134          */
135         socket_options = lp_socket_options();
136         if (socket_options != NULL &&
137             (strstr(socket_options, "SO_SNDBUF") ||
138              strstr(socket_options, "SO_RCVBUF") ||
139              strstr(socket_options, "SO_SNDLOWAT") ||
140              strstr(socket_options, "SO_RCVLOWAT")))
141         {
142                 fprintf(stderr,
143                         "WARNING: socket options = %s\n"
144                         "This warning is printed because you set one of the\n"
145                         "following options: SO_SNDBUF, SO_RCVBUF, SO_SNDLOWAT,\n"
146                         "SO_RCVLOWAT\n"
147                         "Modern server operating systems are tuned for\n"
148                         "high network performance in the majority of situations;\n"
149                         "when you set 'socket options' you are overriding those\n"
150                         "settings.\n"
151                         "Linux in particular has an auto-tuning mechanism for\n"
152                         "buffer sizes (SO_SNDBUF, SO_RCVBUF) that will be\n"
153                         "disabled if you specify a socket buffer size. This can\n"
154                         "potentially cripple your TCP/IP stack.\n\n"
155                         "Getting the 'socket options' correct can make a big\n"
156                         "difference to your performance, but getting them wrong\n"
157                         "can degrade it by just as much. As with any other low\n"
158                         "level setting, if you must make changes to it, make\n "
159                         "small changes and test the effect before making any\n"
160                         "large changes.\n\n",
161                         socket_options);
162         }
163
164         /*
165          * Password server sanity checks.
166          */
167
168         if((lp_security() >= SEC_DOMAIN) && !*lp_passwordserver()) {
169                 const char *sec_setting;
170                 if(lp_security() == SEC_DOMAIN)
171                         sec_setting = "domain";
172                 else if(lp_security() == SEC_ADS)
173                         sec_setting = "ads";
174                 else
175                         sec_setting = "";
176
177                 fprintf(stderr, "ERROR: The setting 'security=%s' requires the "
178                                 "'password server' parameter be set to the "
179                                 "default value * or a valid password server.\n\n",
180                                 sec_setting );
181                 ret = 1;
182         }
183
184         if((lp_security() >= SEC_DOMAIN) && (strcmp(lp_passwordserver(), "*") != 0)) {
185                 const char *sec_setting;
186                 if(lp_security() == SEC_DOMAIN)
187                         sec_setting = "domain";
188                 else if(lp_security() == SEC_ADS)
189                         sec_setting = "ads";
190                 else
191                         sec_setting = "";
192
193                 fprintf(stderr, "WARNING: The setting 'security=%s' should NOT "
194                                 "be combined with the 'password server' "
195                                 "parameter.\n"
196                                 "(by default Samba will discover the correct DC "
197                                 "to contact automatically).\n\n",
198                                 sec_setting );
199         }
200
201         /*
202          * Password chat sanity checks.
203          */
204
205         if(lp_security() == SEC_USER && lp_unix_password_sync()) {
206
207                 /*
208                  * Check that we have a valid lp_passwd_program() if not using pam.
209                  */
210
211 #ifdef WITH_PAM
212                 if (!lp_pam_password_change()) {
213 #endif
214
215                         if((lp_passwd_program(talloc_tos()) == NULL) ||
216                            (strlen(lp_passwd_program(talloc_tos())) == 0))
217                         {
218                                 fprintf(stderr,
219                                         "ERROR: the 'unix password sync' "
220                                         "parameter is set and there is no valid "
221                                         "'passwd program' parameter.\n\n");
222                                 ret = 1;
223                         } else {
224                                 const char *passwd_prog;
225                                 char *truncated_prog = NULL;
226                                 const char *p;
227
228                                 passwd_prog = lp_passwd_program(talloc_tos());
229                                 p = passwd_prog;
230                                 next_token_talloc(talloc_tos(),
231                                                 &p,
232                                                 &truncated_prog, NULL);
233                                 if (truncated_prog && access(truncated_prog, F_OK) == -1) {
234                                         fprintf(stderr,
235                                                 "ERROR: the 'unix password sync' "
236                                                 "parameter is set and the "
237                                                 "'passwd program' (%s) cannot be "
238                                                 "executed (error was %s).\n\n",
239                                                 truncated_prog,
240                                                 strerror(errno));
241                                         ret = 1;
242                                 }
243                         }
244
245 #ifdef WITH_PAM
246                 }
247 #endif
248
249                 if(lp_passwd_chat(talloc_tos()) == NULL) {
250                         fprintf(stderr,
251                                 "ERROR: the 'unix password sync' parameter is "
252                                 "set and there is no valid 'passwd chat' "
253                                 "parameter.\n\n");
254                         ret = 1;
255                 }
256
257                 if ((lp_passwd_program(talloc_tos()) != NULL) &&
258                     (strlen(lp_passwd_program(talloc_tos())) > 0))
259                 {
260                         /* check if there's a %u parameter present */
261                         if(strstr_m(lp_passwd_program(talloc_tos()), "%u") == NULL) {
262                                 fprintf(stderr,
263                                         "ERROR: the 'passwd program' (%s) "
264                                         "requires a '%%u' parameter.\n\n",
265                                         lp_passwd_program(talloc_tos()));
266                                 ret = 1;
267                         }
268                 }
269
270                 /*
271                  * Check that we have a valid script and that it hasn't
272                  * been written to expect the old password.
273                  */
274
275                 if(lp_encrypted_passwords()) {
276                         if(strstr_m( lp_passwd_chat(talloc_tos()), "%o")!=NULL) {
277                                 fprintf(stderr,
278                                         "ERROR: the 'passwd chat' script [%s] "
279                                         "expects to use the old plaintext "
280                                         "password via the %%o substitution. With "
281                                         "encrypted passwords this is not "
282                                         "possible.\n\n",
283                                         lp_passwd_chat(talloc_tos()) );
284                                 ret = 1;
285                         }
286                 }
287         }
288
289         if (strlen(lp_winbind_separator()) != 1) {
290                 fprintf(stderr, "ERROR: the 'winbind separator' parameter must "
291                                 "be a single character.\n\n");
292                 ret = 1;
293         }
294
295         if (*lp_winbind_separator() == '+') {
296                 fprintf(stderr, "'winbind separator = +' might cause problems "
297                                 "with group membership.\n\n");
298         }
299
300         if (lp_algorithmic_rid_base() < BASE_RID) {
301                 /* Try to prevent admin foot-shooting, we can't put algorithmic
302                    rids below 1000, that's the 'well known RIDs' on NT */
303                 fprintf(stderr, "'algorithmic rid base' must be equal to or "
304                                 "above %lu\n\n", BASE_RID);
305         }
306
307         if (lp_algorithmic_rid_base() & 1) {
308                 fprintf(stderr, "'algorithmic rid base' must be even.\n\n");
309         }
310
311 #ifndef HAVE_DLOPEN
312         if (lp_preload_modules()) {
313                 fprintf(stderr, "WARNING: 'preload modules = ' set while loading "
314                                 "plugins not supported.\n\n");
315         }
316 #endif
317
318         if (!lp_passdb_backend()) {
319                 fprintf(stderr, "ERROR: passdb backend must have a value or be "
320                                 "left out\n\n");
321         }
322         
323         if (lp_os_level() > 255) {
324                 fprintf(stderr, "WARNING: Maximum value for 'os level' is "
325                                 "255!\n\n");
326         }
327
328         if (strequal(lp_dos_charset(), "UTF8") || strequal(lp_dos_charset(), "UTF-8")) {
329                 fprintf(stderr, "ERROR: 'dos charset' must not be UTF8\n\n");
330                 ret = 1;
331         }
332
333         return ret;
334 }   
335
336 /**
337  * per-share logic tests
338  */
339 static void do_per_share_checks(int s)
340 {
341         const char **deny_list = lp_hostsdeny(s);
342         const char **allow_list = lp_hostsallow(s);
343         int i;
344
345         if(deny_list) {
346                 for (i=0; deny_list[i]; i++) {
347                         char *hasstar = strchr_m(deny_list[i], '*');
348                         char *hasquery = strchr_m(deny_list[i], '?');
349                         if(hasstar || hasquery) {
350                                 fprintf(stderr,
351                                         "Invalid character %c in hosts deny list "
352                                         "(%s) for service %s.\n\n",
353                                         hasstar ? *hasstar : *hasquery,
354                                         deny_list[i],
355                                         lp_servicename(talloc_tos(), s));
356                         }
357                 }
358         }
359
360         if(allow_list) {
361                 for (i=0; allow_list[i]; i++) {
362                         char *hasstar = strchr_m(allow_list[i], '*');
363                         char *hasquery = strchr_m(allow_list[i], '?');
364                         if(hasstar || hasquery) {
365                                 fprintf(stderr,
366                                         "Invalid character %c in hosts allow "
367                                         "list (%s) for service %s.\n\n",
368                                         hasstar ? *hasstar : *hasquery,
369                                         allow_list[i],
370                                         lp_servicename(talloc_tos(), s));
371                         }
372                 }
373         }
374
375         if(lp_level2_oplocks(s) && !lp_oplocks(s)) {
376                 fprintf(stderr, "Invalid combination of parameters for service "
377                                 "%s. Level II oplocks can only be set if oplocks "
378                                 "are also set.\n\n",
379                                 lp_servicename(talloc_tos(), s));
380         }
381
382         if (!lp_store_dos_attributes(s) && lp_map_hidden(s)
383             && !(lp_create_mask(s) & S_IXOTH))
384         {
385                 fprintf(stderr,
386                         "Invalid combination of parameters for service %s. Map "
387                         "hidden can only work if create mask includes octal "
388                         "01 (S_IXOTH).\n\n",
389                         lp_servicename(talloc_tos(), s));
390         }
391         if (!lp_store_dos_attributes(s) && lp_map_hidden(s)
392             && (lp_force_create_mode(s) & S_IXOTH))
393         {
394                 fprintf(stderr,
395                         "Invalid combination of parameters for service "
396                         "%s. Map hidden can only work if force create mode "
397                         "excludes octal 01 (S_IXOTH).\n\n",
398                         lp_servicename(talloc_tos(), s));
399         }
400         if (!lp_store_dos_attributes(s) && lp_map_system(s)
401             && !(lp_create_mask(s) & S_IXGRP))
402         {
403                 fprintf(stderr,
404                         "Invalid combination of parameters for service "
405                         "%s. Map system can only work if create mask includes "
406                         "octal 010 (S_IXGRP).\n\n",
407                         lp_servicename(talloc_tos(), s));
408         }
409         if (!lp_store_dos_attributes(s) && lp_map_system(s)
410             && (lp_force_create_mode(s) & S_IXGRP))
411         {
412                 fprintf(stderr,
413                         "Invalid combination of parameters for service "
414                         "%s. Map system can only work if force create mode "
415                         "excludes octal 010 (S_IXGRP).\n\n",
416                         lp_servicename(talloc_tos(), s));
417         }
418         if (lp_printing(s) == PRINT_CUPS && *(lp_printcommand(talloc_tos(), s)) != '\0') {
419                 fprintf(stderr,
420                         "Warning: Service %s defines a print command, but "
421                         "parameter is ignored when using CUPS libraries.\n\n",
422                         lp_servicename(talloc_tos(), s));
423         }
424 }
425
426  int main(int argc, const char *argv[])
427 {
428         const char *config_file = get_dyn_CONFIGFILE();
429         int s;
430         static int silent_mode = False;
431         static int show_all_parameters = False;
432         int ret = 0;
433         poptContext pc;
434         static char *parameter_name = NULL;
435         static const char *section_name = NULL;
436         const char *cname;
437         const char *caddr;
438         static int show_defaults;
439         static int skip_logic_checks = 0;
440
441         struct poptOption long_options[] = {
442                 POPT_AUTOHELP
443                 {"suppress-prompt", 's', POPT_ARG_VAL, &silent_mode, 1, "Suppress prompt for enter"},
444                 {"verbose", 'v', POPT_ARG_NONE, &show_defaults, 1, "Show default options too"},
445                 {"skip-logic-checks", 'l', POPT_ARG_NONE, &skip_logic_checks, 1, "Skip the global checks"},
446                 {"show-all-parameters", '\0', POPT_ARG_VAL, &show_all_parameters, True, "Show the parameters, type, possible values" },
447                 {"parameter-name", '\0', POPT_ARG_STRING, &parameter_name, 0, "Limit testparm to a named parameter" },
448                 {"section-name", '\0', POPT_ARG_STRING, &section_name, 0, "Limit testparm to a named section" },
449                 POPT_COMMON_VERSION
450                 POPT_COMMON_DEBUGLEVEL
451                 POPT_COMMON_OPTION
452                 POPT_TABLEEND
453         };
454
455         TALLOC_CTX *frame = talloc_stackframe();
456
457         load_case_tables();
458         /*
459          * Set the default debug level to 2.
460          * Allow it to be overridden by the command line,
461          * not by smb.conf.
462          */
463         lp_set_cmdline("log level", "2");
464
465         pc = poptGetContext(NULL, argc, argv, long_options, 
466                             POPT_CONTEXT_KEEP_FIRST);
467         poptSetOtherOptionHelp(pc, "[OPTION...] <config-file> [host-name] [host-ip]");
468
469         while(poptGetNextOpt(pc) != -1);
470
471         if (show_all_parameters) {
472                 show_parameter_list();
473                 exit(0);
474         }
475
476         setup_logging(poptGetArg(pc), DEBUG_STDERR);
477
478         if (poptPeekArg(pc)) 
479                 config_file = poptGetArg(pc);
480
481         cname = poptGetArg(pc);
482         caddr = poptGetArg(pc);
483
484         poptFreeContext(pc);
485
486         if ( cname && ! caddr ) {
487                 printf ( "ERROR: You must specify both a machine name and an IP address.\n" );
488                 ret = 1;
489                 goto done;
490         }
491
492         fprintf(stderr,"Load smb config files from %s\n",config_file);
493
494         if (!lp_load_with_registry_shares(config_file,False,True,False,True)) {
495                 fprintf(stderr,"Error loading services.\n");
496                 ret = 1;
497                 goto done;
498         }
499
500         fprintf(stderr,"Loaded services file OK.\n");
501
502         if (skip_logic_checks == 0) {
503                 ret = do_global_checks();
504         }
505
506         for (s=0;s<1000;s++) {
507                 if (VALID_SNUM(s))
508                         if (strlen(lp_servicename(talloc_tos(), s)) > 12) {
509                                 fprintf(stderr, "WARNING: You have some share names that are longer than 12 characters.\n" );
510                                 fprintf(stderr, "These may not be accessible to some older clients.\n" );
511                                 fprintf(stderr, "(Eg. Windows9x, WindowsMe, and smbclient prior to Samba 3.0.)\n" );
512                                 break;
513                         }
514         }
515
516         for (s=0;s<1000;s++) {
517                 if (VALID_SNUM(s) && (skip_logic_checks == 0)) {
518                         do_per_share_checks(s);
519                 }
520         }
521
522
523         if (!section_name && !parameter_name) {
524                 fprintf(stderr,
525                         "Server role: %s\n\n",
526                         server_role_str(lp_server_role()));
527         }
528
529         if (!cname) {
530                 if (!silent_mode) {
531                         fprintf(stderr,"Press enter to see a dump of your service definitions\n");
532                         fflush(stdout);
533                         getc(stdin);
534                 }
535                 if (parameter_name || section_name) {
536                         bool isGlobal = False;
537                         s = GLOBAL_SECTION_SNUM;
538
539                         if (!section_name) {
540                                 section_name = GLOBAL_NAME;
541                                 isGlobal = True;
542                         } else if ((isGlobal=!strwicmp(section_name, GLOBAL_NAME)) == 0 &&
543                                  (s=lp_servicenumber(section_name)) == -1) {
544                                         fprintf(stderr,"Unknown section %s\n",
545                                                 section_name);
546                                         ret = 1;
547                                         goto done;
548                         }
549                         if (parameter_name) {
550                                 if (!dump_a_parameter( s, parameter_name, stdout, isGlobal)) {
551                                         fprintf(stderr,"Parameter %s unknown for section %s\n",
552                                                 parameter_name, section_name);
553                                         ret = 1;
554                                         goto done;
555                                 }
556                         } else {
557                                 if (isGlobal == True)
558                                         lp_dump(stdout, show_defaults, 0);
559                                 else
560                                         lp_dump_one(stdout, show_defaults, s);
561                         }
562                         goto done;
563                 }
564
565                 lp_dump(stdout, show_defaults, lp_numservices());
566         }
567
568         if(cname && caddr){
569                 /* this is totally ugly, a real `quick' hack */
570                 for (s=0;s<1000;s++) {
571                         if (VALID_SNUM(s)) {
572                                 if (allow_access(lp_hostsdeny(-1), lp_hostsallow(-1), cname, caddr)
573                                     && allow_access(lp_hostsdeny(s), lp_hostsallow(s), cname, caddr)) {
574                                         fprintf(stderr,"Allow connection from %s (%s) to %s\n",
575                                                    cname,caddr,lp_servicename(talloc_tos(), s));
576                                 } else {
577                                         fprintf(stderr,"Deny connection from %s (%s) to %s\n",
578                                                    cname,caddr,lp_servicename(talloc_tos(), s));
579                                 }
580                         }
581                 }
582         }
583
584 done:
585         gfree_loadparm();
586         TALLOC_FREE(frame);
587         return ret;
588 }
589