Removed version number from file header.
[tprouty/samba.git] / source / nmbd / nmbd_serverlistdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3    NBT netbios routines and daemon - version 2
4    Copyright (C) Andrew Tridgell 1994-1998
5    Copyright (C) Luke Kenneth Casson Leighton 1994-1998
6    Copyright (C) Jeremy Allison 1994-1998
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21    
22 */
23
24 #include "includes.h"
25 #include "smb.h"
26
27 extern int ClientNMB;
28
29 extern fstring global_myworkgroup;
30 extern char **my_netbios_names;
31
32 int updatecount = 0;
33
34 /*******************************************************************
35   Remove all the servers in a work group.
36   ******************************************************************/
37
38 void remove_all_servers(struct work_record *work)
39 {
40   struct server_record *servrec;
41   struct server_record *nexts;
42
43   for (servrec = work->serverlist; servrec; servrec = nexts)
44   {
45     DEBUG(7,("remove_all_servers: Removing server %s\n",servrec->serv.name));
46     nexts = servrec->next;
47
48     if (servrec->prev)
49       servrec->prev->next = servrec->next;
50     if (servrec->next)
51       servrec->next->prev = servrec->prev;
52
53     if (work->serverlist == servrec)
54       work->serverlist = servrec->next;
55
56     ZERO_STRUCTP(servrec);
57     SAFE_FREE(servrec);
58
59   }
60
61   work->subnet->work_changed = True;
62 }
63
64 /***************************************************************************
65   Add a server into the a workgroup serverlist.
66   **************************************************************************/
67
68 static void add_server_to_workgroup(struct work_record *work,
69                              struct server_record *servrec)
70 {
71   struct server_record *servrec2;
72
73   if (!work->serverlist)
74   {
75     work->serverlist = servrec;
76     servrec->prev = NULL;
77     servrec->next = NULL;
78     return;
79   }
80
81   for (servrec2 = work->serverlist; servrec2->next; servrec2 = servrec2->next)
82     ;
83
84   servrec2->next = servrec;
85   servrec->next = NULL;
86   servrec->prev = servrec2;
87   work->subnet->work_changed = True;
88 }
89
90 /****************************************************************************
91   Find a server in a server list.
92   **************************************************************************/
93
94 struct server_record *find_server_in_workgroup(struct work_record *work, char *name)
95 {
96   struct server_record *ret;
97   
98   for (ret = work->serverlist; ret; ret = ret->next)
99   {
100     if (strequal(ret->serv.name,name))
101       return ret;
102   }
103   return NULL;
104 }
105
106
107 /****************************************************************************
108   Remove a server entry from this workgroup.
109   ****************************************************************************/
110
111 void remove_server_from_workgroup(struct work_record *work, struct server_record *servrec)
112 {
113   if (servrec->prev)
114     servrec->prev->next = servrec->next;
115   if (servrec->next)
116     servrec->next->prev = servrec->prev;
117
118   if (work->serverlist == servrec) 
119     work->serverlist = servrec->next; 
120
121   ZERO_STRUCTP(servrec);
122   SAFE_FREE(servrec);
123   work->subnet->work_changed = True;
124 }
125
126 /****************************************************************************
127   Create a server entry on this workgroup.
128   ****************************************************************************/
129
130 struct server_record *create_server_on_workgroup(struct work_record *work,
131                                                  char *name,int servertype, 
132                                                  int ttl,char *comment)
133 {
134   struct server_record *servrec;
135   
136   if (name[0] == '*')
137   {
138       DEBUG(7,("create_server_on_workgroup: not adding name starting with '*' (%s)\n",
139              name));
140       return (NULL);
141   }
142   
143   if((servrec = find_server_in_workgroup(work, name)) != NULL)
144   {
145     DEBUG(0,("create_server_on_workgroup: Server %s already exists on \
146 workgroup %s. This is a bug.\n", name, work->work_group));
147     return NULL;
148   }
149   
150   if((servrec = (struct server_record *)malloc(sizeof(*servrec))) == NULL)
151   {
152     DEBUG(0,("create_server_entry_on_workgroup: malloc fail !\n"));
153     return NULL;
154   }
155
156   memset((char *)servrec,'\0',sizeof(*servrec));
157  
158   servrec->subnet = work->subnet;
159  
160   StrnCpy(servrec->serv.name,name,sizeof(servrec->serv.name)-1);
161   StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1);
162   strupper(servrec->serv.name);
163   servrec->serv.type  = servertype;
164
165   update_server_ttl(servrec, ttl);
166   
167   add_server_to_workgroup(work, servrec);
168       
169   DEBUG(3,("create_server_on_workgroup: Created server entry %s of type %x (%s) on \
170 workgroup %s.\n", name,servertype,comment, work->work_group));
171  
172   work->subnet->work_changed = True;
173  
174   return(servrec);
175 }
176
177 /*******************************************************************
178  Update the ttl field of a server record.
179 *******************************************************************/
180
181 void update_server_ttl(struct server_record *servrec, int ttl)
182 {
183   if(ttl > lp_max_ttl())
184     ttl = lp_max_ttl();
185
186   if(is_myname(servrec->serv.name))
187     servrec->death_time = PERMANENT_TTL;
188   else
189     servrec->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
190
191   servrec->subnet->work_changed = True;
192 }
193
194 /*******************************************************************
195   Expire old servers in the serverlist. A time of -1 indicates 
196   everybody dies except those with a death_time of PERMANENT_TTL (which is 0).
197   This should only be called from expire_workgroups_and_servers().
198   ******************************************************************/
199
200 void expire_servers(struct work_record *work, time_t t)
201 {
202   struct server_record *servrec;
203   struct server_record *nexts;
204   
205   for (servrec = work->serverlist; servrec; servrec = nexts)
206   {
207     nexts = servrec->next;
208
209     if ((servrec->death_time != PERMANENT_TTL) && ((t == -1) || (servrec->death_time < t)))
210     {
211       DEBUG(3,("expire_old_servers: Removing timed out server %s\n",servrec->serv.name));
212       remove_server_from_workgroup(work, servrec);
213       work->subnet->work_changed = True;
214     }
215   }
216 }
217
218 /*******************************************************************
219  Decide if we should write out a server record for this server.
220  We return zero if we should not. Check if we've already written
221  out this server record from an earlier subnet.
222 ******************************************************************/
223
224 static uint32 write_this_server_name( struct subnet_record *subrec,
225                                       struct work_record *work,
226                                       struct server_record *servrec)
227 {
228   struct subnet_record *ssub;
229   struct work_record *iwork;
230
231   /* Go through all the subnets we have already seen. */
232   for (ssub = FIRST_SUBNET; ssub != subrec; ssub = NEXT_SUBNET_INCLUDING_UNICAST(ssub)) 
233   {
234     for(iwork = ssub->workgrouplist; iwork; iwork = iwork->next)
235     {
236       if(find_server_in_workgroup( iwork, servrec->serv.name) != NULL)
237       {
238         /*
239          * We have already written out this server record, don't
240          * do it again. This gives precedence to servers we have seen
241          * on the broadcast subnets over servers that may have been
242          * added via a sync on the unicast_subet.
243          *
244          * The correct way to do this is to have a serverlist file
245          * per subnet - this means changes to smbd as well. I may
246          * add this at a later date (JRA).
247          */
248
249         return 0;
250       }
251     }
252   }
253
254   return servrec->serv.type;
255 }
256
257 /*******************************************************************
258  Decide if we should write out a workgroup record for this workgroup.
259  We return zero if we should not. Don't write out global_myworkgroup (we've
260  already done it) and also don't write out a second workgroup record
261  on the unicast subnet that we've already written out on one of the
262  broadcast subnets.
263 ******************************************************************/
264
265 static uint32 write_this_workgroup_name( struct subnet_record *subrec, 
266                                          struct work_record *work)
267 {
268   struct subnet_record *ssub;
269
270   if(strequal(global_myworkgroup, work->work_group))
271     return 0;
272
273   /* This is a workgroup we have seen on a broadcast subnet. All
274      these have the same type. */
275
276   if(subrec != unicast_subnet)
277     return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY);
278
279   for(ssub = FIRST_SUBNET; ssub;  ssub = NEXT_SUBNET_EXCLUDING_UNICAST(ssub))
280   {
281     /* This is the unicast subnet so check if we've already written out
282        this subnet when we passed over the broadcast subnets. */
283
284     if(find_workgroup_on_subnet( ssub, work->work_group) != NULL)
285       return 0;
286   }
287
288   /* All workgroups on the unicast subnet (except our own, which we
289      have already written out) cannot be local. */
290
291   return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT);
292 }
293
294 /*******************************************************************
295   Write out the browse.dat file.
296   ******************************************************************/
297
298 void write_browse_list_entry(XFILE *fp, fstring name, uint32 rec_type,
299                 fstring local_master_browser_name, fstring description)
300 {
301         fstring tmp;
302
303         slprintf(tmp,sizeof(tmp)-1, "\"%s\"", name);
304         x_fprintf(fp, "%-25s ", tmp);
305         x_fprintf(fp, "%08x ", rec_type);
306         slprintf(tmp, sizeof(tmp)-1, "\"%s\" ", local_master_browser_name);
307         x_fprintf(fp, "%-30s", tmp);
308         x_fprintf(fp, "\"%s\"\n", description);
309 }
310
311 void write_browse_list(time_t t, BOOL force_write)
312 {   
313   struct subnet_record *subrec;
314   struct work_record *work;
315   struct server_record *servrec;
316   pstring fname,fnamenew;
317   uint32 stype;
318   int i;
319   XFILE *fp;
320   BOOL list_changed = force_write;
321   static time_t lasttime = 0;
322     
323   /* Always dump if we're being told to by a signal. */
324   if(force_write == False)
325   {
326     if (!lasttime)
327       lasttime = t;
328     if (t - lasttime < 5)
329       return;
330   }
331
332   lasttime = t;
333
334   dump_workgroups(force_write);
335  
336   for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
337   {
338     if(subrec->work_changed)
339     {
340       list_changed = True;
341       break;
342     }
343   }
344
345   if(!list_changed)
346     return;
347
348   updatecount++;
349     
350   pstrcpy(fname,lp_lockdir());
351   trim_string(fname,NULL,"/");
352   pstrcat(fname,"/");
353   pstrcat(fname,SERVER_LIST);
354   pstrcpy(fnamenew,fname);
355   pstrcat(fnamenew,".");
356  
357   fp = x_fopen(fnamenew,O_WRONLY|O_CREAT|O_TRUNC, 0644);
358  
359   if (!fp)
360   {
361     DEBUG(0,("write_browse_list: Can't open file %s. Error was %s\n",
362               fnamenew,strerror(errno)));
363     return;
364   } 
365   
366   /*
367    * Write out a record for our workgroup. Use the record from the first
368    * subnet.
369    */
370
371   if((work = find_workgroup_on_subnet(FIRST_SUBNET, global_myworkgroup)) == NULL)
372   { 
373     DEBUG(0,("write_browse_list: Fatal error - cannot find my workgroup %s\n",
374              global_myworkgroup));
375     x_fclose(fp);
376     return;
377   }
378
379   write_browse_list_entry(fp, work->work_group,
380      SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY,
381      work->local_master_browser_name, work->work_group);
382
383   /* 
384    * We need to do something special for our own names.
385    * This is due to the fact that we may be a local master browser on
386    * one of our broadcast subnets, and a domain master on the unicast
387    * subnet. We iterate over the subnets and only write out the name
388    * once.
389    */
390
391   for (i=0; my_netbios_names[i]; i++)
392   {
393     stype = 0;
394     for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
395     {
396       if((work = find_workgroup_on_subnet( subrec, global_myworkgroup )) == NULL)
397         continue;
398       if((servrec = find_server_in_workgroup( work, my_netbios_names[i])) == NULL)
399         continue;
400
401       stype |= servrec->serv.type;
402     }
403
404     /* Output server details, plus what workgroup they're in. */
405     write_browse_list_entry(fp, my_netbios_names[i], stype,
406         string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH), global_myworkgroup);
407   }
408       
409   for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) 
410   { 
411     subrec->work_changed = False;
412
413     for (work = subrec->workgrouplist; work ; work = work->next)
414     {
415       /* Write out a workgroup record for a workgroup. */
416       uint32 wg_type = write_this_workgroup_name( subrec, work);
417
418       if(wg_type)
419       {
420         write_browse_list_entry(fp, work->work_group, wg_type,
421                                 work->local_master_browser_name,
422                                 work->work_group);
423       }
424
425       /* Now write out any server records a workgroup may have. */
426
427       for (servrec = work->serverlist; servrec ; servrec = servrec->next)
428       {
429         uint32 serv_type;
430
431         /* We have already written our names here. */
432         if(is_myname(servrec->serv.name))
433           continue; 
434
435         serv_type = write_this_server_name(subrec, work, servrec);
436
437         if(serv_type)
438         {
439           /* Output server details, plus what workgroup they're in. */
440           write_browse_list_entry(fp, servrec->serv.name, serv_type,
441                                  servrec->serv.comment, work->work_group);
442         }
443       }
444     }
445   } 
446   
447   x_fclose(fp);
448   unlink(fname);
449   chmod(fnamenew,0644);
450   rename(fnamenew,fname);
451   DEBUG(3,("write_browse_list: Wrote browse list into file %s\n",fname));
452 }