http://gimel.esc.cam.ac.uk/james/rpld/src/rpld-1.6.tar.gz
[rpld.git] / client.c
1 /*************************************************
2 *     rpld - an IBM style RIPL server            *
3 *************************************************/
4
5 /* Copyright (c) 1999,2000, James McKenzie.
6  *                      All rights reserved
7  * Copyright (c) 1998,2000, Christopher Lightfoot.
8  *                      All rights reserved
9  *
10  * By using this file, you agree to the terms and conditions set
11  * forth in the LICENCE file which can be found at the top level of
12  * the rpld distribution.
13  *
14  * IBM is a trademark of IBM corp.
15  *
16  */
17
18
19 static char rcsid[] =
20   "$Id: client.c,v 1.21 2000/07/29 23:25:58 root Exp root $";
21
22 /*
23  * $Log: client.c,v $
24  * Revision 1.21  2000/07/29 23:25:58  root
25  * #
26  *
27  * Revision 1.20  2000/07/29 23:25:25  root
28  * #
29  *
30  * Revision 1.19  2000/07/23 19:14:19  root
31  * #
32  *
33  * Revision 1.18  2000/07/23 19:07:49  root
34  * #
35  *
36  * Revision 1.17  2000/07/17 10:49:20  root
37  * #
38  *
39  * Revision 1.16  2000/07/17 10:45:38  root
40  * #
41  *
42  * Revision 1.15  2000/07/17 10:43:54  root
43  * #
44  *
45  * Revision 1.14  2000/07/17 10:43:34  root
46  * #
47  *
48  * Revision 1.13  2000/07/16 14:22:06  root
49  * #
50  *
51  * Revision 1.12  2000/07/16 14:05:28  root
52  * #
53  *
54  * Revision 1.11  2000/07/16 13:18:10  root
55  * #
56  *
57  * Revision 1.1  2000/07/16 13:16:33  root
58  * #
59  *
60  * Revision 1.10  1999/09/14 17:12:38  root
61  * #
62  *
63  * Revision 1.9  1999/09/14 17:12:05  root
64  * #
65  *
66  * Revision 1.8  1999/09/13 11:17:35  root
67  * \#
68  *
69  * Revision 1.7  1999/09/13 11:05:27  root
70  * \#
71  *
72  * Revision 1.6  1999/09/13 11:04:13  root
73  * \#
74  *
75  */
76
77 #include "project.h"
78
79 struct client *clients = NULL;
80
81 void
82 cache_locally (struct clfile *f)
83 {
84   int fd;
85   int len;
86
87   fd = open (f->path, O_RDONLY);
88
89   if (fd < 0)
90     {
91       syslog (LOG_ERR, "can't open %s:%m", f->path);
92       return;
93     }
94
95   len = f->length;
96
97   if (len < 0)
98     {
99       len = lseek (fd, 0, SEEK_END);
100       lseek (fd, 0, SEEK_SET);
101     }
102
103   f->data = malloc (len);
104   f->length = len;
105
106   lseek (fd, f->offset, SEEK_SET);
107   read (fd, f->data, len);
108
109   f->offset = 0;
110
111   close (fd);
112
113   free (f->path);
114   f->path = NULL;
115 }
116
117 /* Cache the last client */
118 struct client *
119 find_client_by_mac (unsigned char *mac)
120 {
121   struct client *pc = NULL, *c = clients;
122
123 /* We need to match clients by a (possibly partial) MAC address; however
124  * this struct client is also used to transmit to them, so we need to return
125  * a struct client containing a _real_ MAC address; this is a first and
126  * pretty ugly implementation of this idea.
127  */
128   while (c)
129     {
130       if (memcmp (mac, c->mac, c->partial_mac_len) == 0)
131         {
132           /* if partial match, create new struct client */
133           if (c->partial_mac_len != ETH_ALEN)
134             {
135               pc = (struct client *) malloc (sizeof (struct client));
136               memcpy (pc, c, sizeof (struct client));
137
138               pc->next = c->next;
139               c->next = clients;
140               clients = c;
141
142               return pc;
143             }
144
145           /* else reorder the tree for speed, and return the found client */
146           if (pc)
147             {                   /*Short circuit for next time */
148               pc->next = c->next;
149               c->next = clients;
150               clients = c;
151             }
152           return (c);
153         }
154
155       pc = c;
156       c = c->next;
157     }
158
159   return (NULL);
160 }
161
162 void
163 client_calc_offsets (struct client *c)
164 {
165   struct clfile *f;
166   int bn = 0;
167   int len;
168   int fd;
169   int blocks;
170
171   f = c->files;
172
173   while (f)
174     {
175
176       f->sblock = bn;
177
178       if (f->path)
179         {
180           fd = open (f->path, O_RDONLY);
181           if (fd < 0)
182             {
183               syslog (LOG_ERR, "can't open %s:%m", f->path);
184               return;
185             }
186           len = f->length;
187           if (len < 0)
188             {
189               len = lseek (fd, 0, SEEK_END);
190               len -= f->offset;
191             }
192           close (fd);
193         }
194       else
195         {
196           len = f->length;
197         }
198
199 #ifdef DEBUG
200       printf ("%s is %d bytes long gives", f->path, len);
201 #endif
202       blocks = len / (c->blocklen);
203       len = len - (blocks * c->blocklen);
204
205       if (len == 0)
206         blocks--;
207
208       bn += blocks;
209       f->eblock = bn;
210
211 #ifdef DEBUG
212       printf (" %d blocks [%d,%d]\n", blocks, f->sblock, f->eblock);
213 #endif
214
215       bn++;
216
217       f = f->next;
218
219     }
220
221 }
222
223
224 #define NOTINRANGE(l,v,h) (((v)<(l)) || ((v)>(h)))
225 void
226 client_get_block (struct client *c, struct rpl_packet *p)
227 {
228   struct clfile *f;
229   int toread;
230   int offset;
231
232   f = c->file;
233
234   p->datalen = 0;               /* Some buggy clients request
235                                    * blocks after the end when
236                                    * they get confused */
237
238   if ((!f) || (NOTINRANGE (f->sblock, c->blocknum, f->eblock)))
239     {
240       if ((f) && (f->f))
241         {
242           fclose (f->f);
243           f->f = NULL;
244         }
245       f = c->files;
246       while (f && (f->eblock < c->blocknum))
247         f = f->next;
248
249       if (!f)
250         {
251           syslog (LOG_ERR, "this shouldn't happen oops!");
252           return;
253         }
254
255       if (f->path)
256         {
257           f->f = fopen (f->path, "r");
258           if (!f->f)
259             {
260               c->file = NULL;
261
262               syslog (LOG_ERR, "failed to open %s ", f->path);
263               return;
264             }
265
266         }
267
268       c->file = f;
269     }
270
271
272   offset = (c->blocklen) * (c->blocknum - f->sblock);
273
274   if (f->length > 0)
275     {
276       toread = f->length - offset;
277       if (toread > c->blocklen)
278         toread = c->blocklen;
279     }
280   else
281     {
282       toread = c->blocklen;
283     }
284
285   if (f->path)
286     {                           /* A real file */
287       fseek (f->f, offset + f->offset, SEEK_SET);
288       p->datalen = fread (p->data, 1, toread, f->f);
289       if ((p->datalen != toread) && (c->blocknum != f->eblock))
290         {
291           syslog (LOG_ERR, "short read on %s", f->path);
292         }
293     }
294   else
295     {                           /*cached */
296       bcopy (f->data + offset, p->data, toread);
297       p->datalen = toread;
298     }
299
300   p->addr.load = f->load_addr + offset;
301
302 }
303
304 int
305 client_last_block (struct client *c)
306 {
307   struct clfile *f;
308   f = c->file;
309
310   if ((!f) || (NOTINRANGE (f->sblock, c->blocknum, f->eblock)))
311     {
312       if ((f) && (f->f))
313         {
314           fclose (f->f);
315           f->f = NULL;
316         }
317       c->file = NULL;
318       f = c->files;
319       while (f && (f->eblock < c->blocknum))
320         f = f->next;
321
322       if (!f)
323         {
324           syslog (LOG_ERR, "this shouldn't happen oops!");
325           return 1;             /*Infact past the last block */
326         }
327     }
328
329   if (!(f->next) && (f->eblock == c->blocknum))
330     {
331       return 1;
332     }
333
334
335   return 0;
336 }
337
338 void
339 client_flush_cache (struct client *c)
340 {
341
342   struct clfile *f;
343   f = c->file;
344
345   if ((f) && (f->f))
346     {
347       fclose (f->f);
348       f->f = NULL;
349       c->file = NULL;
350     }
351
352 }
353
354 clients_check_status ()
355 {
356   struct client *c = clients;
357
358   downloading = 0;
359   pacing = 0;
360
361   while (c)
362     {
363       if ((c->state == ST_FILEDATA) && (!c->nospew))
364         {
365           downloading++;
366           if (pacing < c->pacing)
367             pacing = c->pacing;
368         }
369       c = c->next;
370     }
371
372 }
373
374 client_dispatch (struct nit *n)
375 {
376   struct client *c = clients;
377
378   while (c)
379     {
380       if ((c->state == ST_FILEDATA) && (!c->nospew))
381         {
382           file_data_frame (n, c);
383         }
384       c = c->next;
385
386     }
387
388 }