Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / libexec / rbootd / utils.c
1 /*
2  * Copyright (c) 1988, 1992 The University of Utah and the Center
3  *      for Software Science (CSS).
4  * Copyright (c) 1992, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * the Center for Software Science of the University of Utah Computer
9  * Science Department.  CSS requests users of this software to return
10  * to css-dist@cs.utah.edu any improvements that they make and grant
11  * CSS redistribution rights.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *      This product includes software developed by the University of
24  *      California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  *
41  *      from: @(#)utils.c       8.1 (Berkeley) 6/4/93
42  *
43  * From: Utah Hdr: utils.c 3.1 92/07/06
44  * Author: Jeff Forys, University of Utah CSS
45  *
46  * @(#)utils.c  8.1 (Berkeley) 6/4/93
47  * $FreeBSD: src/libexec/rbootd/utils.c,v 1.5 1999/08/28 00:09:46 peter Exp $
48  * $DragonFly: src/libexec/rbootd/utils.c,v 1.2 2003/06/17 04:27:07 dillon Exp $
49  */
50
51 #include <sys/param.h>
52 #include <sys/time.h>
53
54 #include <fcntl.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <syslog.h>
60 #include <time.h>
61 #include <unistd.h>
62 #include "defs.h"
63
64 /*
65 **  DispPkt -- Display the contents of an RMPCONN packet.
66 **
67 **      Parameters:
68 **              rconn - packet to be displayed.
69 **              direct - direction packet is going (DIR_*).
70 **
71 **      Returns:
72 **              Nothing.
73 **
74 **      Side Effects:
75 **              None.
76 */
77 void
78 DispPkt(rconn, direct)
79         RMPCONN *rconn;
80         int direct;
81 {
82         static char BootFmt[] = "\t\tRetCode:%u SeqNo:%lx SessID:%x Vers:%u";
83         static char ReadFmt[] = "\t\tRetCode:%u Offset:%lx SessID:%x\n";
84
85         struct tm *tmp;
86         struct rmp_packet *rmp;
87         int i, omask;
88         u_int32_t t;
89
90         /*
91          *  Since we will be working with RmpConns as well as DbgFp, we
92          *  must block signals that can affect either.
93          */
94         omask = sigblock(sigmask(SIGHUP)|sigmask(SIGUSR1)|sigmask(SIGUSR2));
95
96         if (DbgFp == NULL) {                    /* sanity */
97                 (void) sigsetmask(omask);
98                 return;
99         }
100
101         /* display direction packet is going using '>>>' or '<<<' */
102         fputs((direct==DIR_RCVD)?"<<< ":(direct==DIR_SENT)?">>> ":"", DbgFp);
103
104         /* display packet timestamp */
105         tmp = localtime((time_t *)&rconn->tstamp.tv_sec);
106         fprintf(DbgFp, "%02d:%02d:%02d.%06ld   ", tmp->tm_hour, tmp->tm_min,
107                 tmp->tm_sec, rconn->tstamp.tv_usec);
108
109         /* display src or dst addr and information about network interface */
110         fprintf(DbgFp, "Addr: %s   Intf: %s\n", EnetStr(rconn), IntfName);
111
112         rmp = &rconn->rmp;
113
114         /* display IEEE 802.2 Logical Link Control header */
115         (void) fprintf(DbgFp, "\t802.2 LLC: DSAP:%x SSAP:%x CTRL:%x\n",
116                rmp->hp_llc.dsap, rmp->hp_llc.ssap, ntohs(rmp->hp_llc.cntrl));
117
118         /* display HP extensions to 802.2 Logical Link Control header */
119         (void) fprintf(DbgFp, "\tHP Ext:    DXSAP:%x SXSAP:%x\n",
120                        ntohs(rmp->hp_llc.dxsap), ntohs(rmp->hp_llc.sxsap));
121
122         /*
123          *  Display information about RMP packet using type field to
124          *  determine what kind of packet this is.
125          */
126         switch(rmp->r_type) {
127                 case RMP_BOOT_REQ:              /* boot request */
128                         (void) fprintf(DbgFp, "\tBoot Request:");
129                         GETWORD(rmp->r_brq.rmp_seqno, t);
130                         if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) {
131                                 if (WORDZE(rmp->r_brq.rmp_seqno))
132                                         fputs(" (Send Server ID)", DbgFp);
133                                 else
134                                         fprintf(DbgFp," (Send Filename #%u)",t);
135                         }
136                         (void) fputc('\n', DbgFp);
137                         (void) fprintf(DbgFp, BootFmt, rmp->r_brq.rmp_retcode,
138                                 t, ntohs(rmp->r_brq.rmp_session),
139                                 ntohs(rmp->r_brq.rmp_version));
140                         (void) fprintf(DbgFp, "\n\t\tMachine Type: ");
141                         for (i = 0; i < RMP_MACHLEN; i++)
142                                 (void) fputc(rmp->r_brq.rmp_machtype[i], DbgFp);
143                         DspFlnm(rmp->r_brq.rmp_flnmsize, &rmp->r_brq.rmp_flnm);
144                         break;
145                 case RMP_BOOT_REPL:             /* boot reply */
146                         fprintf(DbgFp, "\tBoot Reply:\n");
147                         GETWORD(rmp->r_brpl.rmp_seqno, t);
148                         (void) fprintf(DbgFp, BootFmt, rmp->r_brpl.rmp_retcode,
149                                 t, ntohs(rmp->r_brpl.rmp_session),
150                                 ntohs(rmp->r_brpl.rmp_version));
151                         DspFlnm(rmp->r_brpl.rmp_flnmsize,&rmp->r_brpl.rmp_flnm);
152                         break;
153                 case RMP_READ_REQ:              /* read request */
154                         (void) fprintf(DbgFp, "\tRead Request:\n");
155                         GETWORD(rmp->r_rrq.rmp_offset, t);
156                         (void) fprintf(DbgFp, ReadFmt, rmp->r_rrq.rmp_retcode,
157                                 t, ntohs(rmp->r_rrq.rmp_session));
158                         (void) fprintf(DbgFp, "\t\tNoOfBytes: %u\n",
159                                 ntohs(rmp->r_rrq.rmp_size));
160                         break;
161                 case RMP_READ_REPL:             /* read reply */
162                         (void) fprintf(DbgFp, "\tRead Reply:\n");
163                         GETWORD(rmp->r_rrpl.rmp_offset, t);
164                         (void) fprintf(DbgFp, ReadFmt, rmp->r_rrpl.rmp_retcode,
165                                 t, ntohs(rmp->r_rrpl.rmp_session));
166                         (void) fprintf(DbgFp, "\t\tNoOfBytesSent: %d\n",
167                                 rconn->rmplen - RMPREADSIZE(0));
168                         break;
169                 case RMP_BOOT_DONE:             /* boot complete */
170                         (void) fprintf(DbgFp, "\tBoot Complete:\n");
171                         (void) fprintf(DbgFp, "\t\tRetCode:%u SessID:%x\n",
172                                 rmp->r_done.rmp_retcode,
173                                 ntohs(rmp->r_done.rmp_session));
174                         break;
175                 default:                        /* ??? */
176                         (void) fprintf(DbgFp, "\tUnknown Type:(%d)\n",
177                                 rmp->r_type);
178         }
179         (void) fputc('\n', DbgFp);
180         (void) fflush(DbgFp);
181
182         (void) sigsetmask(omask);               /* reset old signal mask */
183 }
184
185
186 /*
187 **  GetEtherAddr -- convert an RMP (Ethernet) address into a string.
188 **
189 **      An RMP BOOT packet has been received.  Look at the type field
190 **      and process Boot Requests, Read Requests, and Boot Complete
191 **      packets.  Any other type will be dropped with a warning msg.
192 **
193 **      Parameters:
194 **              addr - array of RMP_ADDRLEN bytes.
195 **
196 **      Returns:
197 **              Pointer to static string representation of `addr'.
198 **
199 **      Side Effects:
200 **              None.
201 **
202 **      Warnings:
203 **              - The return value points to a static buffer; it must
204 **                be copied if it's to be saved.
205 */
206 char *
207 GetEtherAddr(addr)
208         u_int8_t *addr;
209 {
210         static char Hex[] = "0123456789abcdef";
211         static char etherstr[RMP_ADDRLEN*3];
212         int i;
213         char *cp;
214
215         /*
216          *  For each byte in `addr', convert it to "<hexchar><hexchar>:".
217          *  The last byte does not get a trailing `:' appended.
218          */
219         i = 0;
220         cp = etherstr;
221         for(;;) {
222                 *cp++ = Hex[*addr >> 4 & 0xf];
223                 *cp++ = Hex[*addr++ & 0xf];
224                 if (++i == RMP_ADDRLEN)
225                         break;
226                 *cp++ = ':';
227         }
228         *cp = '\0';
229
230         return(etherstr);
231 }
232
233
234 /*
235 **  DispFlnm -- Print a string of bytes to DbgFp (often, a file name).
236 **
237 **      Parameters:
238 **              size - number of bytes to print.
239 **              flnm - address of first byte.
240 **
241 **      Returns:
242 **              Nothing.
243 **
244 **      Side Effects:
245 **              - Characters are sent to `DbgFp'.
246 */
247 void
248 DspFlnm(size, flnm)
249         u_int size;
250         char *flnm;
251 {
252         int i;
253
254         (void) fprintf(DbgFp, "\n\t\tFile Name (%u): <", size);
255         for (i = 0; i < size; i++)
256                 (void) fputc(*flnm++, DbgFp);
257         (void) fputs(">\n", DbgFp);
258 }
259
260
261 /*
262 **  NewClient -- allocate memory for a new CLIENT.
263 **
264 **      Parameters:
265 **              addr - RMP (Ethernet) address of new client.
266 **
267 **      Returns:
268 **              Ptr to new CLIENT or NULL if we ran out of memory.
269 **
270 **      Side Effects:
271 **              - Memory will be malloc'd for the new CLIENT.
272 **              - If malloc() fails, a log message will be generated.
273 */
274 CLIENT *
275 NewClient(addr)
276         u_int8_t *addr;
277 {
278         CLIENT *ctmp;
279
280         if ((ctmp = (CLIENT *) malloc(sizeof(CLIENT))) == NULL) {
281                 syslog(LOG_ERR, "NewClient: out of memory (%s)",
282                        GetEtherAddr(addr));
283                 return(NULL);
284         }
285
286         memset(ctmp, 0, sizeof(CLIENT));
287         memmove(&ctmp->addr[0], addr, RMP_ADDRLEN);
288         return(ctmp);
289 }
290
291 /*
292 **  FreeClient -- free linked list of Clients.
293 **
294 **      Parameters:
295 **              None.
296 **
297 **      Returns:
298 **              Nothing.
299 **
300 **      Side Effects:
301 **              - All malloc'd memory associated with the linked list of
302 **                CLIENTS will be free'd; `Clients' will be set to NULL.
303 **
304 **      Warnings:
305 **              - This routine must be called with SIGHUP blocked.
306 */
307 void
308 FreeClients()
309 {
310         CLIENT *ctmp;
311
312         while (Clients != NULL) {
313                 ctmp = Clients;
314                 Clients = Clients->next;
315                 FreeClient(ctmp);
316         }
317 }
318
319 /*
320 **  NewStr -- allocate memory for a character array.
321 **
322 **      Parameters:
323 **              str - null terminated character array.
324 **
325 **      Returns:
326 **              Ptr to new character array or NULL if we ran out of memory.
327 **
328 **      Side Effects:
329 **              - Memory will be malloc'd for the new character array.
330 **              - If malloc() fails, a log message will be generated.
331 */
332 char *
333 NewStr(str)
334         char *str;
335 {
336         char *stmp;
337
338         if ((stmp = (char *)malloc((unsigned) (strlen(str)+1))) == NULL) {
339                 syslog(LOG_ERR, "NewStr: out of memory (%s)", str);
340                 return(NULL);
341         }
342
343         (void) strcpy(stmp, str);
344         return(stmp);
345 }
346
347 /*
348 **  To save time, NewConn and FreeConn maintain a cache of one RMPCONN
349 **  in `LastFree' (defined below).
350 */
351
352 static RMPCONN *LastFree = NULL;
353
354 /*
355 **  NewConn -- allocate memory for a new RMPCONN connection.
356 **
357 **      Parameters:
358 **              rconn - initialization template for new connection.
359 **
360 **      Returns:
361 **              Ptr to new RMPCONN or NULL if we ran out of memory.
362 **
363 **      Side Effects:
364 **              - Memory may be malloc'd for the new RMPCONN (if not cached).
365 **              - If malloc() fails, a log message will be generated.
366 */
367 RMPCONN *
368 NewConn(rconn)
369         RMPCONN *rconn;
370 {
371         RMPCONN *rtmp;
372
373         if (LastFree == NULL) {         /* nothing cached; make a new one */
374                 if ((rtmp = (RMPCONN *) malloc(sizeof(RMPCONN))) == NULL) {
375                         syslog(LOG_ERR, "NewConn: out of memory (%s)",
376                                EnetStr(rconn));
377                         return(NULL);
378                 }
379         } else {                        /* use the cached RMPCONN */
380                 rtmp = LastFree;
381                 LastFree = NULL;
382         }
383
384         /*
385          *  Copy template into `rtmp', init file descriptor to `-1' and
386          *  set ptr to next elem NULL.
387          */
388         memmove((char *)rtmp, (char *)rconn, sizeof(RMPCONN));
389         rtmp->bootfd = -1;
390         rtmp->next = NULL;
391
392         return(rtmp);
393 }
394
395 /*
396 **  FreeConn -- Free memory associated with an RMPCONN connection.
397 **
398 **      Parameters:
399 **              rtmp - ptr to RMPCONN to be free'd.
400 **
401 **      Returns:
402 **              Nothing.
403 **
404 **      Side Effects:
405 **              - Memory associated with `rtmp' may be free'd (or cached).
406 **              - File desc associated with `rtmp->bootfd' will be closed.
407 */
408 void
409 FreeConn(rtmp)
410         RMPCONN *rtmp;
411 {
412         /*
413          *  If the file descriptor is in use, close the file.
414          */
415         if (rtmp->bootfd >= 0) {
416                 (void) close(rtmp->bootfd);
417                 rtmp->bootfd = -1;
418         }
419
420         if (LastFree == NULL)           /* cache for next time */
421                 rtmp = LastFree;
422         else                            /* already one cached; free this one */
423                 free((char *)rtmp);
424 }
425
426 /*
427 **  FreeConns -- free linked list of RMPCONN connections.
428 **
429 **      Parameters:
430 **              None.
431 **
432 **      Returns:
433 **              Nothing.
434 **
435 **      Side Effects:
436 **              - All malloc'd memory associated with the linked list of
437 **                connections will be free'd; `RmpConns' will be set to NULL.
438 **              - If LastFree is != NULL, it too will be free'd & NULL'd.
439 **
440 **      Warnings:
441 **              - This routine must be called with SIGHUP blocked.
442 */
443 void
444 FreeConns()
445 {
446         RMPCONN *rtmp;
447
448         while (RmpConns != NULL) {
449                 rtmp = RmpConns;
450                 RmpConns = RmpConns->next;
451                 FreeConn(rtmp);
452         }
453
454         if (LastFree != NULL) {
455                 free((char *)LastFree);
456                 LastFree = NULL;
457         }
458 }
459
460 /*
461 **  AddConn -- Add a connection to the linked list of connections.
462 **
463 **      Parameters:
464 **              rconn - connection to be added.
465 **
466 **      Returns:
467 **              Nothing.
468 **
469 **      Side Effects:
470 **              - RmpConn will point to new connection.
471 **
472 **      Warnings:
473 **              - This routine must be called with SIGHUP blocked.
474 */
475 void
476 AddConn(rconn)
477         RMPCONN *rconn;
478 {
479         if (RmpConns != NULL)
480                 rconn->next = RmpConns;
481         RmpConns = rconn;
482 }
483
484 /*
485 **  FindConn -- Find a connection in the linked list of connections.
486 **
487 **      We use the RMP (Ethernet) address as the basis for determining
488 **      if this is the same connection.  According to the Remote Maint
489 **      Protocol, we can only have one connection with any machine.
490 **
491 **      Parameters:
492 **              rconn - connection to be found.
493 **
494 **      Returns:
495 **              Matching connection from linked list or NULL if not found.
496 **
497 **      Side Effects:
498 **              None.
499 **
500 **      Warnings:
501 **              - This routine must be called with SIGHUP blocked.
502 */
503 RMPCONN *
504 FindConn(rconn)
505         RMPCONN *rconn;
506 {
507         RMPCONN *rtmp;
508
509         for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next)
510                 if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0],
511                          (char *)&rtmp->rmp.hp_hdr.saddr[0], RMP_ADDRLEN) == 0)
512                         break;
513
514         return(rtmp);
515 }
516
517 /*
518 **  RemoveConn -- Remove a connection from the linked list of connections.
519 **
520 **      Parameters:
521 **              rconn - connection to be removed.
522 **
523 **      Returns:
524 **              Nothing.
525 **
526 **      Side Effects:
527 **              - If found, an RMPCONN will cease to exist and it will
528 **                be removed from the linked list.
529 **
530 **      Warnings:
531 **              - This routine must be called with SIGHUP blocked.
532 */
533 void
534 RemoveConn(rconn)
535         RMPCONN *rconn;
536 {
537         RMPCONN *thisrconn, *lastrconn;
538
539         if (RmpConns == rconn) {                /* easy case */
540                 RmpConns = RmpConns->next;
541                 FreeConn(rconn);
542         } else {                                /* must traverse linked list */
543                 lastrconn = RmpConns;                   /* set back ptr */
544                 thisrconn = lastrconn->next;            /* set current ptr */
545                 while (thisrconn != NULL) {
546                         if (rconn == thisrconn) {               /* found it */
547                                 lastrconn->next = thisrconn->next;
548                                 FreeConn(thisrconn);
549                                 break;
550                         }
551                         lastrconn = thisrconn;
552                         thisrconn = thisrconn->next;
553                 }
554         }
555 }