modified become_master() to a state-based system. becoming a master
[samba.git] / source / nmbd / nmbd.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    NBT netbios routines and daemon - version 2
5    Copyright (C) Andrew Tridgell 1994-1995
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21    Revision History:
22
23    14 jan 96: lkcl@pires.co.uk
24    added multiple workgroup domain master support
25
26 */
27
28 #include "includes.h"
29
30 extern int DEBUGLEVEL;
31
32 extern pstring debugf;
33 pstring servicesf = CONFIGFILE;
34
35 extern pstring scope;
36
37 int ClientNMB   = -1;
38 int ClientDGRAM = -1;
39
40 extern pstring myhostname;
41 static pstring host_file;
42 extern pstring myname;
43
44 /* are we running as a daemon ? */
45 static BOOL is_daemon = False;
46
47 /* machine comment for host announcements */
48 pstring ServerComment="";
49
50 /* what server type are we currently */
51
52 time_t StartupTime =0;
53
54 extern struct in_addr ipzero;
55
56
57  /****************************************************************************
58   catch a sigterm
59   ****************************************************************************/
60 static int sig_term()
61 {
62   BlockSignals(True);
63   
64   DEBUG(0,("Got SIGTERM: going down...\n"));
65   
66   dump_names();
67   reload_services(True);
68   
69   /* remove all samba names, with wins server if necessary. */
70   remove_my_names();
71   
72   /* announce all server entries as 0 time-to-live, 0 type */
73   remove_my_servers();
74
75   /* XXXX don't care if we never receive a response back... yet */
76   /* XXXX other things: if we are a master browser, force an election? */
77   
78   exit(0);
79 }
80
81
82 /****************************************************************************
83 catch a sighup
84 ****************************************************************************/
85 static int sig_hup(void)
86 {
87   BlockSignals(True);
88
89   DEBUG(0,("Got SIGHUP (reload not implemented)\n"));
90   dump_names();
91   reload_services(True);
92
93   BlockSignals(False);
94 #ifndef DONT_REINSTALL_SIG
95   signal(SIGHUP,SIGNAL_CAST sig_hup);
96 #endif
97   return(0);
98 }
99
100 /****************************************************************************
101 catch a sigpipe
102 ****************************************************************************/
103 static int sig_pipe(void)
104 {
105   BlockSignals(True);
106
107   DEBUG(0,("Got SIGPIPE\n"));
108   if (!is_daemon)
109     exit(1);
110   BlockSignals(False);
111   return(0);
112 }
113
114 #if DUMP_CORE
115 /*******************************************************************
116 prepare to dump a core file - carefully!
117 ********************************************************************/
118 static BOOL dump_core(void)
119 {
120   char *p;
121   pstring dname;
122   strcpy(dname,debugf);
123   if ((p=strrchr(dname,'/'))) *p=0;
124   strcat(dname,"/corefiles");
125   mkdir(dname,0700);
126   sys_chown(dname,getuid(),getgid());
127   chmod(dname,0700);
128   if (chdir(dname)) return(False);
129   umask(~(0700));
130
131 #ifndef NO_GETRLIMIT
132 #ifdef RLIMIT_CORE
133   {
134     struct rlimit rlp;
135     getrlimit(RLIMIT_CORE, &rlp);
136     rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
137     setrlimit(RLIMIT_CORE, &rlp);
138     getrlimit(RLIMIT_CORE, &rlp);
139     DEBUG(3,("Core limits now %d %d\n",rlp.rlim_cur,rlp.rlim_max));
140   }
141 #endif
142 #endif
143
144
145   DEBUG(0,("Dumping core in %s\n",dname));
146   return(True);
147 }
148 #endif
149
150
151 /****************************************************************************
152 possibly continue after a fault
153 ****************************************************************************/
154 static void fault_continue(void)
155 {
156 #if DUMP_CORE
157   dump_core();
158 #endif
159 }
160
161 /*******************************************************************
162   expire old names from the namelist and server list
163   ******************************************************************/
164 static void expire_names_and_servers(void)
165 {
166   static time_t lastrun = 0;
167   time_t t = time(NULL);
168   
169   if (!lastrun) lastrun = t;
170   if (t < lastrun + 5) return;
171   lastrun = t;
172   
173   expire_names(t);
174   expire_servers(t);
175 }
176
177 /*****************************************************************************
178   reload the services file
179   **************************************************************************/
180 BOOL reload_services(BOOL test)
181 {
182   BOOL ret;
183   extern fstring remote_machine;
184
185   strcpy(remote_machine,"nmbd");
186
187   if (lp_loaded())
188     {
189       pstring fname;
190       strcpy(fname,lp_configfile());
191       if (file_exist(fname,NULL) && !strcsequal(fname,servicesf))
192         {
193           strcpy(servicesf,fname);
194           test = False;
195         }
196     }
197
198   if (test && !lp_file_list_changed())
199     return(True);
200
201   ret = lp_load(servicesf,True);
202
203   /* perhaps the config filename is now set */
204   if (!test) {
205     DEBUG(3,("services not loaded\n"));
206     reload_services(True);
207   }
208
209   load_interfaces();
210   add_subnet_interfaces();
211
212   return(ret);
213 }
214
215
216
217 /****************************************************************************
218 load a netbios hosts file
219 ****************************************************************************/
220 static void load_hosts_file(char *fname)
221 {
222   FILE *f = fopen(fname,"r");
223   pstring line;
224   if (!f) {
225     DEBUG(2,("Can't open lmhosts file %s\n",fname));
226     return;
227   }
228
229   while (!feof(f))
230     {
231       if (!fgets_slash(line,sizeof(pstring),f)) continue;
232
233       if (*line == '#') continue;
234
235       {
236         BOOL group=False;
237
238         pstring ip,name,mask,flags,extra;
239
240         char *ptr;
241         int count = 0;
242         struct in_addr ipaddr;
243         struct in_addr ipmask;
244         enum name_source source = LMHOSTS;
245
246         strcpy(ip,"");
247         strcpy(name,"");
248         strcpy(mask,"");
249         strcpy(flags,"");
250         strcpy(extra,"");
251         
252         ptr = line;
253
254         if (next_token(&ptr,ip   ,NULL)) ++count;
255         if (next_token(&ptr,name ,NULL)) ++count;
256         if (next_token(&ptr,mask ,NULL)) ++count;
257         if (next_token(&ptr,flags,NULL)) ++count;
258         if (next_token(&ptr,extra,NULL)) ++count;
259
260         if (count <= 0) continue;
261
262         if (count > 0 && count < 2) {
263           DEBUG(0,("Ill formed hosts line [%s]\n",line));           
264           continue;
265         }
266
267         /* work out if we need to shuffle the tokens along due to the
268            optional subnet mask argument */
269
270         if (strchr(mask, 'G') || strchr(mask, 'S') || strchr(mask, 'M')) {
271           strcpy(flags, mask );
272           /* default action for no subnet mask */
273           strcpy(mask, "");
274         }
275
276         DEBUG(4, ("lmhost entry: %s %s %s %s\n", ip, name, mask, flags));
277
278         if (strchr(flags,'G') || strchr(flags,'S'))
279           group = True;
280
281         if (strchr(flags,'M') && !group) {
282           source = SELF;
283           strcpy(myname,name);
284         }
285
286         ipaddr = *interpret_addr2(ip);
287         if (*mask)
288           ipmask = *interpret_addr2(mask);
289         else 
290           ipmask = *iface_nmask(ipaddr);
291
292         if (group) {
293           add_subnet_entry(ipaddr, ipmask, name, True, True);
294         } else {
295       struct subnet_record *d = find_subnet(ipaddr);
296       if (d)
297       {
298             add_netbios_entry(d,name,0x20,NB_ACTIVE,0,source,ipaddr,True,True);
299       }
300         }
301       }
302     }
303
304   fclose(f);
305 }
306
307
308 /****************************************************************************
309   The main select loop.
310   ***************************************************************************/
311 static void process(void)
312 {
313   BOOL run_election;
314
315   while (True)
316     {
317       time_t t = time(NULL);
318       run_election = check_elections();
319       listen_for_packets(run_election);
320
321       run_packet_queue();
322       run_elections();
323
324       announce_host();
325
326 #if 0
327       /* XXXX what was this stuff supposed to do? It sent
328          ANN_GetBackupListReq packets which I think should only be
329          sent when trying to find out who to browse with */      
330
331       announce_backup();
332 #endif
333
334       announce_master();
335
336       query_refresh_names();
337
338       expire_names_and_servers();
339       expire_netbios_response_entries();
340       refresh_my_names(t);
341
342       write_browse_list();
343       do_browser_lists();
344       check_master_browser();
345     }
346 }
347
348
349 /****************************************************************************
350   open the socket communication
351 ****************************************************************************/
352 static BOOL open_sockets(BOOL isdaemon, int port)
353 {
354   struct hostent *hp;
355  
356   /* get host info */
357   if ((hp = Get_Hostbyname(myhostname)) == 0) {
358     DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",myhostname));
359     return False;
360   }   
361
362   if (isdaemon)
363     ClientNMB = open_socket_in(SOCK_DGRAM, port,0);
364   else
365     ClientNMB = 0;
366   
367   ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3);
368
369   if (ClientNMB == -1)
370     return(False);
371
372   signal(SIGPIPE, SIGNAL_CAST sig_pipe);
373
374   set_socket_options(ClientNMB,"SO_BROADCAST");
375   set_socket_options(ClientDGRAM,"SO_BROADCAST");
376
377   DEBUG(3,("Sockets opened.\n"));
378   return True;
379 }
380
381
382 /****************************************************************************
383   initialise connect, service and file structs
384 ****************************************************************************/
385 static BOOL init_structs()
386 {
387   if (!get_myname(myhostname,NULL))
388     return(False);
389
390   if (! *myname) {
391     char *p;
392     strcpy(myname,myhostname);
393     p = strchr(myname,'.');
394     if (p) *p = 0;
395   }
396
397   return True;
398 }
399
400 /****************************************************************************
401 usage on the program
402 ****************************************************************************/
403 static void usage(char *pname)
404 {
405   DEBUG(0,("Incorrect program usage - is the command line correct?\n"));
406
407   printf("Usage: %s [-n name] [-B bcast address] [-D] [-p port] [-d debuglevel] [-l log basename]\n",pname);
408   printf("Version %s\n",VERSION);
409   printf("\t-D                    become a daemon\n");
410   printf("\t-p port               listen on the specified port\n");
411   printf("\t-d debuglevel         set the debuglevel\n");
412   printf("\t-l log basename.      Basename for log/debug files\n");
413   printf("\t-n netbiosname.       the netbios name to advertise for this host\n");
414   printf("\t-B broadcast address  the address to use for broadcasts\n");
415   printf("\t-N netmask           the netmask to use for subnet determination\n");
416   printf("\t-H hosts file        load a netbios hosts file\n");
417   printf("\t-G group name        add a group name to be part of\n");
418   printf("\t-I ip-address        override the IP address\n");
419   printf("\t-C comment           sets the machine comment that appears in browse lists\n");
420   printf("\n");
421 }
422
423
424 /****************************************************************************
425   main program
426   **************************************************************************/
427  int main(int argc,char *argv[])
428 {
429   int port = NMB_PORT;
430   int opt;
431   extern FILE *dbf;
432   extern char *optarg;
433   fstring group;
434
435   *group = 0;
436   *host_file = 0;
437
438   StartupTime = time(NULL);
439
440   TimeInit();
441
442   strcpy(debugf,NMBLOGFILE);
443
444   setup_logging(argv[0],False);
445
446   charset_initialise();
447
448 #ifdef LMHOSTSFILE
449   strcpy(host_file,LMHOSTSFILE);
450 #endif
451
452   /* this is for people who can't start the program correctly */
453   while (argc > 1 && (*argv[1] != '-')) {
454     argv++;
455     argc--;
456   }
457
458   fault_setup(fault_continue);
459
460   signal(SIGHUP ,SIGNAL_CAST sig_hup);
461   signal(SIGTERM,SIGNAL_CAST sig_term);
462
463   while ((opt = getopt(argc, argv, "s:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G:")) != EOF)
464     {
465       switch (opt)
466         {
467         case 's':
468           strcpy(servicesf,optarg);
469           break;
470         case 'C':
471           strcpy(ServerComment,optarg);
472           break;
473         case 'G':
474           strcpy(group,optarg);
475           break;
476         case 'H':
477           strcpy(host_file,optarg);
478           break;
479         case 'I':
480           iface_set_default(optarg,NULL,NULL);
481           break;
482         case 'B':
483           iface_set_default(NULL,optarg,NULL);
484           break;
485         case 'N':
486           iface_set_default(NULL,NULL,optarg);
487           break;
488         case 'n':
489           strcpy(myname,optarg);
490           break;
491         case 'l':
492           sprintf(debugf,"%s.nmb",optarg);
493           break;
494         case 'i':
495           strcpy(scope,optarg);
496           strupper(scope);
497           break;
498         case 'D':
499           is_daemon = True;
500           break;
501         case 'd':
502           DEBUGLEVEL = atoi(optarg);
503           break;
504         case 'p':
505           port = atoi(optarg);
506           break;
507         case 'h':
508           usage(argv[0]);
509           exit(0);
510           break;
511         default:
512           if (!is_a_socket(0)) {
513             usage(argv[0]);
514           }
515           break;
516         }
517     }
518
519   DEBUG(1,("%s netbios nameserver version %s started\n",timestring(),VERSION));
520   DEBUG(1,("Copyright Andrew Tridgell 1994\n"));
521
522   init_structs();
523
524   if (!reload_services(False))
525     return(-1); 
526
527   if (*group)
528     add_my_subnets(group);
529
530   if (!is_daemon && !is_a_socket(0)) {
531     DEBUG(0,("standard input is not a socket, assuming -D option\n"));
532     is_daemon = True;
533   }
534   
535   if (is_daemon) {
536     DEBUG(2,("%s becoming a daemon\n",timestring()));
537     become_daemon();
538   }
539
540   DEBUG(3,("Opening sockets %d\n", port));
541
542   if (!open_sockets(is_daemon,port)) return 1;
543
544   if (*host_file) {
545     load_hosts_file(host_file);
546     DEBUG(3,("Loaded hosts file\n"));
547   }
548
549
550
551   if (!*ServerComment)
552     strcpy(ServerComment,"Samba %v");
553   string_sub(ServerComment,"%v",VERSION);
554   string_sub(ServerComment,"%h",myhostname);
555
556   add_my_names();
557   add_my_subnets(lp_workgroup());
558
559   DEBUG(3,("Checked names\n"));
560   
561   load_netbios_names();
562
563   DEBUG(3,("Loaded names\n"));
564
565   write_browse_list();
566
567   DEBUG(3,("Dumped names\n"));
568
569   process();
570   close_sockets();
571
572   if (dbf)
573     fclose(dbf);
574   return(0);
575 }