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