- remove some incorrect prototypes from server.c
[kai/samba.git] / source3 / nameelect.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 #include "loadparm.h"
30 #include "localnet.h"
31
32 extern int DEBUGLEVEL;
33 extern pstring scope;
34
35 extern pstring myname;
36
37 /* machine comment for host announcements */
38 extern  pstring ServerComment;
39
40 /* here are my election parameters */
41
42 extern time_t StartupTime;
43
44 #define AM_MASTER(work) (work->ServerType & SV_TYPE_MASTER_BROWSER)
45
46 #define MSBROWSE "\001\002__MSBROWSE__\002"
47 #define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
48
49 extern struct domain_record *domainlist;
50
51
52 /*******************************************************************
53   occasionally check to see if the master browser is around
54   ******************************************************************/
55 void check_master_browser(void)
56 {
57   static time_t lastrun=0;
58   time_t t = time(NULL);
59   struct domain_record *d;
60
61   if (!lastrun) lastrun = t;
62   if (t < lastrun + 2*60) return;
63   lastrun = t;
64
65   dump_workgroups();
66
67   for (d = domainlist; d; d = d->next)
68     {
69       struct work_record *work;
70
71       for (work = d->workgrouplist; work; work = work->next)
72         {
73           /* if we are not the browse master of a workgroup, and we can't
74              find a browser on the subnet, do something about it. */
75
76           if (!AM_MASTER(work))
77             {
78               queue_netbios_packet(ClientNMB,NMB_QUERY,CHECK_MASTER,
79                                    work->work_group,0x1d,0,
80                                    True,False,d->bcast_ip);
81             }
82         }
83     }
84 }
85
86
87 /*******************************************************************
88   what to do if a master browser DOESN't exist
89   ******************************************************************/
90 void browser_gone(char *work_name, struct in_addr ip)
91 {
92   struct domain_record *d = find_domain(ip);
93   struct work_record *work = find_workgroupstruct(d, work_name, False);
94
95   if (!work || !d) return;
96
97   DEBUG(2,("Forcing election on %s\n",work->work_group));
98
99   if (strequal(work->work_group, lp_workgroup()) &&
100       ip_equal(bcast_ip, d->bcast_ip))
101     {
102       /* we can attempt to become master browser */
103       work->needelection = True;
104     }
105   else
106     {
107       DEBUG(2,("no master browser for persistent entry %s %s\n",
108                work->work_group, inet_ntoa(d->bcast_ip)));
109       
110       /* XXXX oh dear. we are going to have problems here. the
111          entry is a persistent one, there isn't anyone responsible
112          for this workgroup up and running, yet we can't find it
113          and we are going to continually have name_queries until
114          a master browser is found for this workgroup on the
115          remote subnet.
116          */
117     }
118 }
119 /****************************************************************************
120   send an election packet
121   **************************************************************************/
122 void send_election(struct domain_record *d, char *group,uint32 criterion,
123                    int timeup,char *name)
124 {
125   pstring outbuf;
126   char *p;
127
128   if (!d) return;
129   
130   DEBUG(2,("Sending election to %s for workgroup %s\n",
131            inet_ntoa(d->bcast_ip),group));         
132
133   bzero(outbuf,sizeof(outbuf));
134   p = outbuf;
135   CVAL(p,0) = 8; /* election */
136   p++;
137
138   CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION;
139   SIVAL(p,1,criterion);
140   SIVAL(p,5,timeup*1000); /* ms - despite the spec */
141   p += 13;
142   strcpy(p,name);
143   strupper(p);
144   p = skip_string(p,1);
145   
146   send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
147                       name,group,0,0x1e,d->bcast_ip,myip);
148 }
149
150
151 /*******************************************************************
152   become the master browser
153   ******************************************************************/
154 static void become_master(struct domain_record *d, struct work_record *work)
155 {
156   uint32 domain_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_SERVER_UNIX | 0x00400000;
157
158   if (!work) return;
159   
160   DEBUG(2,("Becoming master for %s\n",work->work_group));
161   
162   work->ServerType |= SV_TYPE_MASTER_BROWSER;
163   work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
164   work->ElectionCriterion |= 0x5;
165   
166   /* add browse, master and general names to database or register with WINS */
167   add_name_entry(MSBROWSE        ,0x01,NB_ACTIVE|NB_GROUP);
168   add_name_entry(work->work_group,0x1d,NB_ACTIVE         );
169   
170   if (lp_domain_master())
171     {
172       DEBUG(4,("Domain master: adding names...\n"));
173       
174       /* add domain master and domain member names or register with WINS */
175       add_name_entry(work->work_group,0x1b,NB_ACTIVE         );
176       add_name_entry(work->work_group,0x1c,NB_ACTIVE|NB_GROUP);
177       
178       work->ServerType |= SV_TYPE_DOMAIN_MASTER;
179       
180       if (lp_domain_logons())
181         {
182           work->ServerType |= SV_TYPE_DOMAIN_CTRL;
183           work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
184         }
185     }
186   
187   /* update our server status */
188   add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
189   add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
190   
191   if (ip_equal(bcast_ip, d->bcast_ip))
192     {
193       /* ask all servers on our local net to announce to us */
194       announce_request(work, d->bcast_ip);
195     }
196 }
197
198
199 /*******************************************************************
200   unbecome the master browser
201   ******************************************************************/
202 void become_nonmaster(struct domain_record *d, struct work_record *work)
203 {
204   DEBUG(2,("Becoming non-master for %s\n",work->work_group));
205   
206   work->ServerType &= ~SV_TYPE_MASTER_BROWSER;
207   work->ServerType &= ~SV_TYPE_DOMAIN_MASTER;
208   work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
209   
210   work->ElectionCriterion &= ~0x4;
211   
212   remove_name_entry(work->work_group,0x1b);
213   remove_name_entry(work->work_group,0x1c);
214   remove_name_entry(work->work_group,0x1d);
215   remove_name_entry(MSBROWSE        ,0x01);
216 }
217
218
219 /*******************************************************************
220   run the election
221   ******************************************************************/
222 void run_elections(void)
223 {
224   time_t t = time(NULL);
225   static time_t lastime = 0;
226   
227   struct domain_record *d;
228   
229   /* send election packets once a second */
230   if (lastime && t-lastime <= 0) return;
231   
232   lastime = t;
233   
234   for (d = domainlist; d; d = d->next)
235     {
236       struct work_record *work;
237       for (work = d->workgrouplist; work; work = work->next)
238         {
239           if (work->RunningElection)
240             {
241               send_election(d,work->work_group, work->ElectionCriterion,
242                             t-StartupTime,myname);
243               
244               if (work->ElectionCount++ >= 4)
245                 {
246                   /* I won! now what :-) */
247                   DEBUG(2,(">>> Won election on %s <<<\n",work->work_group));
248                   
249                   work->RunningElection = False;
250                   become_master(d, work);
251                 }
252             }
253         }
254     }
255 }
256
257
258 /*******************************************************************
259   work out if I win an election
260   ******************************************************************/
261 static BOOL win_election(struct work_record *work,int version,uint32 criterion,
262                          int timeup,char *name)
263 {  
264   time_t t = time(NULL);
265   uint32 mycriterion;
266   if (version > ELECTION_VERSION) return(False);
267   if (version < ELECTION_VERSION) return(True);
268   
269   mycriterion = work->ElectionCriterion;
270
271   if (criterion > mycriterion) return(False);
272   if (criterion < mycriterion) return(True);
273
274   if (timeup > (t - StartupTime)) return(False);
275   if (timeup < (t - StartupTime)) return(True);
276
277   if (strcasecmp(myname,name) > 0) return(False);
278   
279   return(True);
280 }
281
282
283 /*******************************************************************
284   process a election packet
285
286   An election dynamically decides who will be the master. 
287   ******************************************************************/
288 void process_election(struct packet_struct *p,char *buf)
289 {
290   struct dgram_packet *dgram = &p->packet.dgram;
291   struct in_addr ip = dgram->header.source_ip;
292   struct domain_record *d = find_domain(ip);
293   int version = CVAL(buf,0);
294   uint32 criterion = IVAL(buf,1);
295   int timeup = IVAL(buf,5)/1000;
296   char *name = buf+13;
297   struct work_record *work;
298
299   if (!d) return;
300   
301   name[15] = 0;  
302
303   DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
304            name,version,criterion,timeup));
305   
306   if (same_context(dgram)) return;
307   
308   for (work = d->workgrouplist; work; work = work->next)
309     {
310       if (listening_name(work, &dgram->dest_name) && 
311           strequal(work->work_group, lp_workgroup()) &&
312           ip_equal(d->bcast_ip, bcast_ip))
313         {
314           if (win_election(work, version,criterion,timeup,name))
315             {
316               if (!work->RunningElection)
317                 {
318                   work->needelection = True;
319                   work->ElectionCount=0;
320                 }
321             }
322           else
323             {
324               work->needelection = False;
325               
326               if (work->RunningElection)
327                 {
328                   work->RunningElection = False;
329                   DEBUG(3,(">>> Lost election on %s <<<\n",work->work_group));
330                   
331                   /* if we are the master then remove our masterly names */
332                   if (AM_MASTER(work))
333                     {
334                       become_nonmaster(d, work);
335                     }
336                 }
337             }
338         }
339     }
340 }
341
342
343 /****************************************************************************
344   checks whether a browser election is to be run on any workgroup
345   ***************************************************************************/
346 BOOL check_elections(void)
347 {
348         struct domain_record *d;
349         BOOL run_any_election = False;
350
351         for (d = domainlist; d; d = d->next)
352         {
353                 struct work_record *work;
354                 for (work = d->workgrouplist; work; work = work->next)
355                 {
356                         run_any_election |= work->RunningElection;
357
358                         if (work->needelection && !work->RunningElection)
359                         {
360                                 DEBUG(3,(">>> Starting election on %s <<<\n",work->work_group));
361                                 work->ElectionCount = 0;
362                                 work->RunningElection = True;
363                                 work->needelection = False;
364                         }
365                 }
366         }
367         return run_any_election;
368 }
369