patch to fix NetServerEnum with multiple workgroup lists kindly supplied.
[Samba.git] / source / nameelect.c
blobc841d9b7a60a9dddd67900335cf1e8581190fd47
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
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.
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.
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.
21 Revision History:
23 14 jan 96: lkcl@pires.co.uk
24 added multiple workgroup domain master support
28 #include "includes.h"
30 extern int ClientNMB;
31 extern int ClientDGRAM;
33 extern int DEBUGLEVEL;
34 extern pstring scope;
36 extern pstring myname;
38 /* machine comment for host announcements */
39 extern pstring ServerComment;
41 /* here are my election parameters */
43 extern time_t StartupTime;
45 #define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
47 extern struct subnet_record *subnetlist;
50 /*******************************************************************
51 occasionally check to see if the master browser is around
52 ******************************************************************/
53 void check_master_browser(void)
55 static time_t lastrun=0;
56 time_t t = time(NULL);
57 struct subnet_record *d;
59 if (!lastrun) lastrun = t;
60 if (t < lastrun + CHECK_TIME_MST_BROWSE * 60)
61 return;
62 lastrun = t;
64 dump_workgroups();
66 for (d = subnetlist; d; d = d->next)
68 struct work_record *work;
70 for (work = d->workgrouplist; work; work = work->next)
72 /* if we are not the browse master of a workgroup, and we can't
73 find a browser on the subnet, do something about it. */
75 if (!AM_MASTER(work))
77 queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK,
78 work->work_group,0x1d,0,0,
79 True,False,d->bcast_ip);
86 /*******************************************************************
87 what to do if a master browser DOESN't exist
88 ******************************************************************/
89 void browser_gone(char *work_name, struct in_addr ip)
91 struct subnet_record *d = find_subnet(ip);
92 struct work_record *work = find_workgroupstruct(d, work_name, False);
94 if (!work || !d) return;
96 if (strequal(work->work_group, lp_workgroup()) &&
97 ismybcast(d->bcast_ip))
100 DEBUG(2,("Forcing election on %s %s\n",
101 work->work_group,inet_ntoa(d->bcast_ip)));
103 /* we can attempt to become master browser */
104 work->needelection = True;
106 else
108 /* XXXX note: this will delete entries that have been added in by
109 lmhosts as well. a flag to ensure that these are not deleted may
110 be considered */
112 /* workgroup with no master browser is not the default workgroup:
113 it's also not on our subnet. therefore delete it: it can be
114 recreated dynamically */
116 send_election(d, work->work_group, 0, 0, myname);
117 remove_workgroup(d, work);
122 /****************************************************************************
123 send an election packet
124 **************************************************************************/
125 void send_election(struct subnet_record *d, char *group,uint32 criterion,
126 int timeup,char *name)
128 pstring outbuf;
129 char *p;
131 if (!d) return;
133 DEBUG(2,("Sending election to %s for workgroup %s\n",
134 inet_ntoa(d->bcast_ip),group));
136 bzero(outbuf,sizeof(outbuf));
137 p = outbuf;
138 CVAL(p,0) = 8; /* election */
139 p++;
141 CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION;
142 SIVAL(p,1,criterion);
143 SIVAL(p,5,timeup*1000); /* ms - despite the spec */
144 p += 13;
145 strcpy(p,name);
146 strupper(p);
147 p = skip_string(p,1);
149 send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
150 name,group,0,0x1e,d->bcast_ip,*iface_ip(d->bcast_ip));
154 /*******************************************************************
155 become the master browser
156 ******************************************************************/
157 static void become_master(struct subnet_record *d, struct work_record *work)
159 uint32 domain_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_SERVER_UNIX | 0x00400000;
161 if (!work) return;
163 DEBUG(2,("Becoming master for %s\n",work->work_group));
165 work->ServerType |= SV_TYPE_MASTER_BROWSER;
166 work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
167 work->ElectionCriterion |= 0x5;
169 /* add browse, master and general names to database or register with WINS */
170 add_my_name_entry(d,MSBROWSE ,0x01,NB_ACTIVE|NB_GROUP);
171 add_my_name_entry(d,work->work_group,0x1d,NB_ACTIVE );
173 if (lp_domain_master())
175 DEBUG(4,("Domain master: adding names...\n"));
177 /* add domain master and domain member names or register with WINS */
178 add_my_name_entry(d,work->work_group,0x1b,NB_ACTIVE );
180 work->ServerType |= SV_TYPE_DOMAIN_MASTER;
182 if (lp_domain_logons())
184 work->ServerType |= SV_TYPE_DOMAIN_CTRL;
185 work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
189 /* update our server status */
190 add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
191 add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
193 if (d->my_interface)
195 /* ask all servers on our local net to announce to us */
196 announce_request(work, d->bcast_ip);
201 /*******************************************************************
202 unbecome the master browser. initates removal of necessary netbios
203 names, and tells the world that we are no longer a master browser.
204 ******************************************************************/
205 void become_nonmaster(struct subnet_record *d, struct work_record *work,
206 int remove_type)
208 int new_server_type = work->ServerType;
210 DEBUG(2,("Becoming non-master for %s\n",work->work_group));
212 /* can only remove master or domain types with this function */
213 remove_type &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
215 /* unbecome a master browser; unbecome a domain master, too :-( */
216 if (remove_type & SV_TYPE_MASTER_BROWSER)
217 remove_type |= SV_TYPE_DOMAIN_MASTER;
219 new_server_type &= ~remove_type;
221 if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
223 /* no longer a master browser of any sort */
225 work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
226 work->ElectionCriterion &= ~0x4;
228 /* announce ourselves as no longer active as a master browser. */
229 announce_server(d, work, work->work_group, myname, 0, 0);
230 remove_name_entry(d,MSBROWSE ,0x01);
233 work->ServerType = new_server_type;
235 if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
236 remove_name_entry(d,work->work_group,0x1b);
238 if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
239 remove_name_entry(d,work->work_group,0x1d);
243 /*******************************************************************
244 run the election
245 ******************************************************************/
246 void run_elections(void)
248 time_t t = time(NULL);
249 static time_t lastime = 0;
251 struct subnet_record *d;
253 /* send election packets once a second */
254 if (lastime && t-lastime <= 0) return;
256 lastime = t;
258 for (d = subnetlist; d; d = d->next)
260 struct work_record *work;
261 for (work = d->workgrouplist; work; work = work->next)
263 if (work->RunningElection)
265 send_election(d,work->work_group, work->ElectionCriterion,
266 t-StartupTime,myname);
268 if (work->ElectionCount++ >= 4)
270 /* I won! now what :-) */
271 DEBUG(2,(">>> Won election on %s %s <<<\n",
272 work->work_group,inet_ntoa(d->bcast_ip)));
274 work->RunningElection = False;
275 become_master(d, work);
283 /*******************************************************************
284 work out if I win an election
285 ******************************************************************/
286 static BOOL win_election(struct work_record *work,int version,uint32 criterion,
287 int timeup,char *name)
289 time_t t = time(NULL);
290 uint32 mycriterion;
291 if (version > ELECTION_VERSION) return(False);
292 if (version < ELECTION_VERSION) return(True);
294 mycriterion = work->ElectionCriterion;
296 if (criterion > mycriterion) return(False);
297 if (criterion < mycriterion) return(True);
299 if (timeup > (t - StartupTime)) return(False);
300 if (timeup < (t - StartupTime)) return(True);
302 if (strcasecmp(myname,name) > 0) return(False);
304 return(True);
308 /*******************************************************************
309 process a election packet
311 An election dynamically decides who will be the master.
312 ******************************************************************/
313 void process_election(struct packet_struct *p,char *buf)
315 struct dgram_packet *dgram = &p->packet.dgram;
316 struct in_addr ip = dgram->header.source_ip;
317 struct subnet_record *d = find_subnet(ip);
318 int version = CVAL(buf,0);
319 uint32 criterion = IVAL(buf,1);
320 int timeup = IVAL(buf,5)/1000;
321 char *name = buf+13;
322 struct work_record *work;
324 if (!d) return;
326 name[15] = 0;
328 DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
329 name,version,criterion,timeup));
331 if (same_context(dgram)) return;
333 for (work = d->workgrouplist; work; work = work->next)
335 if (listening_name(work, &dgram->dest_name) &&
336 strequal(work->work_group, lp_workgroup()) &&
337 d->my_interface)
339 if (win_election(work, version,criterion,timeup,name))
341 if (!work->RunningElection)
343 work->needelection = True;
344 work->ElectionCount=0;
347 else
349 work->needelection = False;
351 if (work->RunningElection)
353 work->RunningElection = False;
354 DEBUG(3,(">>> Lost election on %s %s <<<\n",
355 work->work_group,inet_ntoa(d->bcast_ip)));
357 /* if we are the master then remove our masterly names */
358 if (AM_MASTER(work))
360 become_nonmaster(d, work,
361 SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
370 /****************************************************************************
371 checks whether a browser election is to be run on any workgroup
372 ***************************************************************************/
373 BOOL check_elections(void)
375 struct subnet_record *d;
376 BOOL run_any_election = False;
378 for (d = subnetlist; d; d = d->next)
380 struct work_record *work;
381 for (work = d->workgrouplist; work; work = work->next)
383 run_any_election |= work->RunningElection;
385 if (work->needelection && !work->RunningElection)
387 DEBUG(3,(">>> Starting election on %s %s <<<\n",
388 work->work_group,inet_ntoa(d->bcast_ip)));
389 work->ElectionCount = 0;
390 work->RunningElection = True;
391 work->needelection = False;
395 return run_any_election;