This is *not* a big change (although it looks like one).
[samba.git] / source3 / nmbd / nmbd_elections.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-1998
6    Copyright (C) Luke Kenneth Casson Leighton 1994-1998
7    Copyright (C) Jeremy Allison 1994-1998
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 2 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, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22    
23 */
24
25 #include "includes.h"
26
27 extern int DEBUGLEVEL;
28
29 extern pstring myname;
30 extern fstring myworkgroup;
31
32 /* Election parameters. */
33 extern time_t StartupTime;
34
35 /****************************************************************************
36   Send an election datagram packet.
37 **************************************************************************/
38 static void send_election_dgram(struct subnet_record *subrec, char *workgroup_name,
39                                 uint32 criterion, int timeup,char *server_name)
40 {
41   pstring outbuf;
42   char *p;
43
44   DEBUG(2,("send_election_dgram: Sending election packet for workgroup %s on subnet %s\n",
45         workgroup_name, subrec->subnet_name ));
46
47   bzero(outbuf,sizeof(outbuf));
48   p = outbuf;
49   CVAL(p,0) = ANN_Election; /* Election opcode. */
50   p++;
51
52   CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION;
53   SIVAL(p,1,criterion);
54   SIVAL(p,5,timeup*1000); /* ms - Despite what the spec says. */
55   p += 13;
56   pstrcpy(p,server_name);
57   strupper(p);
58   p = skip_string(p,1);
59   
60   send_mailslot(False, BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
61                 server_name, 0,
62                 workgroup_name, 0x1e,
63                 subrec->bcast_ip, subrec->myip);
64 }
65
66 /*******************************************************************
67  We found a current master browser on one of our broadcast interfaces.
68 ******************************************************************/
69
70 static void check_for_master_browser_success(struct subnet_record *subrec,
71                                  struct userdata_struct *userdata,
72                                  struct nmb_name *answer_name,
73                                  struct in_addr answer_ip, struct res_rec *rrec)
74 {
75   DEBUG(3,("check_for_master_browser_success: Local master browser for workgroup %s exists at \
76 IP %s (just checking).\n", answer_name->name, inet_ntoa(answer_ip) ));
77 }
78
79 /*******************************************************************
80  We failed to find a current master browser on one of our broadcast interfaces.
81 ******************************************************************/
82
83 static void check_for_master_browser_fail( struct subnet_record *subrec,
84                                            struct response_record *rrec,
85                                            struct nmb_name *question_name,
86                                            int fail_code)
87 {
88   char *workgroup_name = question_name->name;
89   struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name);
90
91   if(work == NULL)
92   {
93     DEBUG(0,("check_for_master_browser_fail: Unable to find workgroup %s on subnet %s.=\n",
94           workgroup_name, subrec->subnet_name ));
95     return;
96   }
97
98   if (strequal(work->work_group, myworkgroup))
99   {
100
101     if (lp_local_master())
102     {
103       /* We have discovered that there is no local master
104          browser, and we are configured to initiate
105          an election that we will participate in.
106        */
107       DEBUG(2,("check_for_master_browser_fail: Forcing election on workgroup %s subnet %s\n",
108                work->work_group, subrec->subnet_name ));
109
110       /* Setting this means we will participate when the
111          election is run in run_elections(). */
112       work->needelection = True;
113     }
114     else
115     {
116       /* We need to force an election, because we are configured
117          not to become the local master, but we still need one,
118          having detected that one doesn't exist.
119        */
120       send_election_dgram(subrec, work->work_group, 0, 0, myname);
121     }
122   }
123 }
124
125 /*******************************************************************
126   Ensure there is a local master browser for a workgroup on our
127   broadcast interfaces.
128 ******************************************************************/
129
130 void check_master_browser_exists(time_t t)
131 {
132   static time_t lastrun=0;
133   struct subnet_record *subrec;
134   char *workgroup_name = myworkgroup;
135
136   if (!lastrun)
137     lastrun = t;
138
139   if (t < (lastrun + (CHECK_TIME_MST_BROWSE * 60)))
140     return;
141
142   lastrun = t;
143
144   dump_workgroups(False);
145
146   for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
147   {
148     struct work_record *work;
149
150     for (work = subrec->workgrouplist; work; work = work->next)
151     {
152       if (strequal(work->work_group, workgroup_name) && !AM_LOCAL_MASTER_BROWSER(work))
153       {
154         /* Do a name query for the local master browser on this net. */
155         query_name( subrec, work->work_group, 0x1d,
156                     check_for_master_browser_success,
157                     check_for_master_browser_fail,
158                     NULL);
159       }
160     }
161   }
162 }
163
164 /*******************************************************************
165   Run an election.
166 ******************************************************************/
167
168 void run_elections(time_t t)
169 {
170   static time_t lastime = 0;
171   
172   struct subnet_record *subrec;
173   
174   /* Send election packets once a second - note */
175   if (lastime && (t - lastime <= 0))
176     return;
177   
178   lastime = t;
179   
180   for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
181   {
182     struct work_record *work;
183
184     for (work = subrec->workgrouplist; work; work = work->next)
185     {
186       if (work->RunningElection)
187       {
188         send_election_dgram(subrec, work->work_group, work->ElectionCriterion,
189                       t - StartupTime, myname);
190               
191         if (work->ElectionCount++ >= 4)
192         {
193           /* Won election (4 packets were sent out uncontested. */
194           DEBUG(2,("run_elections: >>> Won election for workgroup %s on subnet %s <<<\n",
195                     work->work_group, subrec->subnet_name ));
196
197           work->RunningElection = False;
198
199           become_local_master_browser(subrec, work);
200         }
201       }
202     }
203   }
204 }
205
206 /*******************************************************************
207   Determine if I win an election.
208 ******************************************************************/
209
210 static BOOL win_election(struct work_record *work, int version,
211                          uint32 criterion, int timeup, char *server_name)
212 {  
213   int mytimeup = time(NULL) - StartupTime;
214   uint32 mycriterion = work->ElectionCriterion;
215
216   /* If local master is false then never win
217      in election broadcasts. */
218   if(!lp_local_master())
219   {
220     DEBUG(3,("win_election: Losing election as local master == False\n"));
221     return False;
222   }
223  
224   DEBUG(4,("win_election: election comparison: %x:%x %x:%x %d:%d %s:%s\n",
225         version, ELECTION_VERSION,
226         criterion, mycriterion,
227         timeup, mytimeup,
228         server_name, myname));
229
230   if (version > ELECTION_VERSION)
231     return(False);
232   if (version < ELECTION_VERSION)
233     return(True);
234   
235   if (criterion > mycriterion)
236     return(False);
237   if (criterion < mycriterion)
238     return(True);
239
240   if (timeup > mytimeup)
241     return(False);
242   if (timeup < mytimeup)
243     return(True);
244
245   if (strcasecmp(myname, server_name) > 0)
246     return(False);
247   
248   return(True);
249 }
250
251 /*******************************************************************
252   Process an incoming election datagram packet.
253 ******************************************************************/
254
255 void process_election(struct subnet_record *subrec, struct packet_struct *p, char *buf)
256 {
257   struct dgram_packet *dgram = &p->packet.dgram;
258   int version = CVAL(buf,0);
259   uint32 criterion = IVAL(buf,1);
260   int timeup = IVAL(buf,5)/1000;
261   char *server_name = buf+13;
262   struct work_record *work;
263   char *workgroup_name = dgram->dest_name.name;
264
265   server_name[15] = 0;  
266
267   DEBUG(3,("process_election: Election request from %s at IP %s on subnet %s for workgroup %s.\n",
268       server_name,inet_ntoa(p->ip), subrec->subnet_name, workgroup_name ));
269
270   DEBUG(5,("process_election: vers=%d criterion=%08x timeup=%d\n", version,criterion,timeup));
271
272   if(( work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL)
273   {
274     DEBUG(0,("process_election: Cannot find workgroup %s on subnet %s.\n",
275           workgroup_name, subrec->subnet_name ));
276     return;
277   }
278
279   if (!strequal(work->work_group, myworkgroup))
280   {
281     DEBUG(3,("process_election: ignoring election request for workgroup %s on subnet %s as this \
282 is not my workgroup.\n", work->work_group, subrec->subnet_name ));
283     return;
284   }
285
286   if (win_election(work, version,criterion,timeup,server_name))
287   {
288     /* We take precedence over the requesting server. */
289     if (!work->RunningElection)
290     {
291       /* We weren't running an election - start running one. */
292
293       work->needelection = True;
294       work->ElectionCount=0;
295     }
296
297     /* Note that if we were running an election for this workgroup on this
298        subnet already, we just ignore the server we take precedence over. */
299   }
300   else
301   {
302     /* We lost. Stop participating. */
303     work->needelection = False;
304
305     if (work->RunningElection || AM_LOCAL_MASTER_BROWSER(work))
306     {
307       work->RunningElection = False;
308       DEBUG(3,("process_election: >>> Lost election for workgroup %s on subnet %s <<<\n",
309             work->work_group, subrec->subnet_name ));
310       if (AM_LOCAL_MASTER_BROWSER(work))
311         unbecome_local_master_browser(subrec, work);
312     }
313   }
314 }
315
316 /****************************************************************************
317   This function looks over all the workgroups known on all the broadcast
318   subnets and decides if a browser election is to be run on that workgroup.
319   It returns True if any election packets need to be sent (this will then
320   be done by run_elections().
321 ***************************************************************************/
322
323 BOOL check_elections(void)
324 {
325   struct subnet_record *subrec;
326   BOOL run_any_election = False;
327
328   for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
329   {
330     struct work_record *work;
331     for (work = subrec->workgrouplist; work; work = work->next)
332     {
333       run_any_election |= work->RunningElection;
334
335       /* Only start an election if we are in the potential browser state. */
336       if (work->needelection && !work->RunningElection && AM_POTENTIAL_MASTER_BROWSER(work))
337       {
338         DEBUG(3,("check_elections: >>> Starting election for workgroup %s on subnet %s <<<\n",
339               work->work_group, subrec->subnet_name ));
340
341         work->ElectionCount = 0;
342         work->RunningElection = True;
343         work->needelection = False;
344       }
345     }
346   }
347   return run_any_election;
348 }