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