source: lib/format_rt.c @ e502f76

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since e502f76 was e502f76, checked in by Shane Alcock <salcock@…>, 15 years ago

Added a pause function to format_rt
Moved the rt_connect call to rt_start
Got rid of blocking in rt_read_versatile by remembering the rt header
Removed 'restrict' from trace.c as some compilers didn't like it
Fixed bugs in configure that was causing failure to detect DAG
Added a new rt type for lost connections

  • Property mode set to 100644
File size: 18.1 KB
Line 
1/*
2 * This file is part of libtrace
3 *
4 * Copyright (c) 2004 The University of Waikato, Hamilton, New Zealand.
5 * Authors: Daniel Lawson
6 *          Perry Lorier
7 *          Shane Alcock
8 *
9 * All rights reserved.
10 *
11 * This code has been developed by the University of Waikato WAND
12 * research group. For further information please see http://www.wand.net.nz/
13 *
14 * libtrace is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * libtrace is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with libtrace; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27 *
28 * $Id$
29 *
30 */
31
32#define _GNU_SOURCE
33
34#include "config.h"
35#include "common.h"
36#include "libtrace.h"
37#include "libtrace_int.h"
38#include "format_helper.h"
39#include "parse_cmd.h"
40#include "rt_protocol.h"
41
42#include <sys/stat.h>
43#include <assert.h>
44#include <errno.h>
45#include <fcntl.h>
46#include <stdio.h>
47#include <string.h>
48#include <stdlib.h>
49
50#ifndef WIN32
51# include <netdb.h>
52#endif
53
54#define RT_INFO ((struct rt_format_data_t*)libtrace->format_data)
55
56char *rt_deny_reason(uint8_t reason) {
57        char *string = 0;
58
59        switch(reason) {
60                case RT_DENY_WRAPPER:
61                        string = "Rejected by TCP Wrappers";
62                        break;
63                case RT_DENY_FULL:
64                        string = "Max connections reached on server";
65                        break;
66                case RT_DENY_AUTH:
67                        string = "Authentication failed";
68                        break;
69                default:
70                        string = "Unknown reason";
71        }
72
73        return string;
74}
75
76
77struct rt_format_data_t {
78        char *hostname;
79        int port;
80        int input_fd;
81        int reliable;
82        char *pkt_buffer;
83        char *buf_current;
84        int buf_filled;
85
86        libtrace_t *dummy_duck;
87        libtrace_t *dummy_erf;
88        libtrace_t *dummy_pcap;
89        libtrace_t *dummy_wag;
90        libtrace_t *dummy_linux;
91};
92
93static int rt_connect(libtrace_t *libtrace) {
94        struct hostent *he;
95        struct sockaddr_in remote;
96        rt_header_t connect_msg;
97        rt_deny_conn_t deny_hdr;       
98        rt_hello_t hello_opts;
99        uint8_t reason;
100       
101        if ((he=gethostbyname(RT_INFO->hostname)) == NULL) {
102                trace_set_err(libtrace, TRACE_ERR_INIT_FAILED,
103                                "Failed to convert hostname %s to address",
104                                RT_INFO->hostname);
105                return -1;
106        }
107        if ((RT_INFO->input_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
108                trace_set_err(libtrace, TRACE_ERR_INIT_FAILED,
109                                "Could not create socket");
110                return -1;
111        }
112
113        remote.sin_family = AF_INET;
114        remote.sin_port = htons(RT_INFO->port);
115        remote.sin_addr = *((struct in_addr *)he->h_addr);
116        memset(&(remote.sin_zero), 0, 8);
117
118        if (connect(RT_INFO->input_fd, (struct sockaddr *)&remote,
119                                sizeof(struct sockaddr)) == -1) {
120                trace_set_err(libtrace, TRACE_ERR_INIT_FAILED,
121                                "Could not connect to host %s on port %d",
122                                RT_INFO->hostname, RT_INFO->port);
123                return -1;
124        }
125
126       
127#if 0
128        oldflags = fcntl(RT_INFO->input_fd, F_GETFL, 0);
129        if (oldflags == -1) {
130                trace_set_err(libtrace, errno,
131                                "Could not get fd flags from fd %d\n",
132                                RT_INFO->input_fd);
133                return -1;
134        }
135        oldflags |= O_NONBLOCK;
136        if (fcntl(RT_INFO->input_fd, F_SETFL, oldflags) == -1) {
137                trace_set_err(libtrace, errno,
138                                "Could not set fd flags for fd %d\n",
139                                RT_INFO->input_fd);
140                return -1;
141        }
142#endif
143       
144       
145        /* We are connected, now receive message from server */
146       
147        if (recv(RT_INFO->input_fd, (void*)&connect_msg, sizeof(rt_header_t), 0) != sizeof(rt_header_t) ) {
148                trace_set_err(libtrace, TRACE_ERR_INIT_FAILED,
149                                "Could not receive connection message from %s",
150                                RT_INFO->hostname);
151                return -1;
152        }
153       
154        switch (connect_msg.type) {
155                case RT_DENY_CONN:
156                       
157                        if (recv(RT_INFO->input_fd, (void*)&deny_hdr, 
158                                                sizeof(rt_deny_conn_t),
159                                                0) != sizeof(rt_deny_conn_t)) {
160                                reason = 0;
161                        }       
162                        reason = deny_hdr.reason;
163                        trace_set_err(libtrace, TRACE_ERR_INIT_FAILED,
164                                "Connection attempt is denied: %s",
165                                rt_deny_reason(reason));       
166                        return -1;
167                case RT_HELLO:
168                        /* do something with options */
169                        if (recv(RT_INFO->input_fd, (void*)&hello_opts, 
170                                                sizeof(rt_hello_t), 0)
171                                        != sizeof(rt_hello_t)) {
172                                trace_set_err(libtrace, TRACE_ERR_INIT_FAILED,
173                                        "Failed to receive RT_HELLO options");
174                                return -1;
175                        }
176                        RT_INFO->reliable = hello_opts.reliable;
177                       
178                        return 0;
179                default:
180                        trace_set_err(libtrace, TRACE_ERR_INIT_FAILED,
181                                        "Unknown message type received: %d",
182                                        connect_msg.type);
183                        return -1;
184        }
185        trace_set_err(libtrace, TRACE_ERR_INIT_FAILED,
186                        "Somehow you managed to reach this unreachable code");
187        return -1;
188}
189
190
191static int rt_init_input(libtrace_t *libtrace) {
192        char *scan;
193        char *uridata = libtrace->uridata;
194        libtrace->format_data = malloc(sizeof(struct rt_format_data_t));
195
196        RT_INFO->dummy_duck = NULL;
197        RT_INFO->dummy_erf = NULL;
198        RT_INFO->dummy_pcap = NULL;
199        RT_INFO->dummy_wag = NULL;
200        RT_INFO->dummy_linux = NULL;
201        RT_INFO->pkt_buffer = NULL;
202        RT_INFO->buf_current = NULL;
203        RT_INFO->buf_filled = 0;
204       
205        if (strlen(uridata) == 0) {
206                RT_INFO->hostname =
207                        strdup("localhost");
208                RT_INFO->port =
209                        COLLECTOR_PORT;
210        } else {
211                if ((scan = strchr(uridata,':')) == NULL) {
212                        RT_INFO->hostname =
213                                strdup(uridata);
214                        RT_INFO->port =
215                                COLLECTOR_PORT;
216                } else {
217                        RT_INFO->hostname =
218                                (char *)strndup(uridata,
219                                                (scan - uridata));
220                        RT_INFO->port =
221                                atoi(++scan);
222                }
223        }
224
225        return 0;
226}
227       
228static int rt_start_input(libtrace_t *libtrace) {
229        rt_header_t start_msg;
230
231        start_msg.type = RT_START;
232        start_msg.length = 0; 
233
234        if (rt_connect(libtrace) == -1)
235                return -1;
236       
237        /* Need to send start message to server */
238        if (send(RT_INFO->input_fd, (void*)&start_msg, sizeof(rt_header_t) +
239                                start_msg.length, 0) != sizeof(rt_header_t)) {
240                printf("Failed to send start message to server\n");
241                return -1;
242        }
243
244        return 0;
245}
246
247static int rt_pause_input(libtrace_t *libtrace) {
248        close(RT_INFO->input_fd);
249        return 0;
250}
251
252static int rt_fin_input(libtrace_t *libtrace) {
253        rt_header_t close_msg;
254
255        close_msg.type = RT_CLOSE;
256        close_msg.length = 0; 
257       
258        /* Send a close message to the server */
259        if (send(RT_INFO->input_fd, (void*)&close_msg, sizeof(rt_header_t) + 
260                                close_msg.length, 0) != sizeof(rt_header_t)
261                                + close_msg.length) {
262                printf("Failed to send close message to server\n");
263       
264        }
265        if (RT_INFO->dummy_duck)
266                trace_destroy_dead(RT_INFO->dummy_duck);
267
268        if (RT_INFO->dummy_erf) 
269                trace_destroy_dead(RT_INFO->dummy_erf);
270               
271        if (RT_INFO->dummy_pcap)
272                trace_destroy_dead(RT_INFO->dummy_pcap);
273
274        if (RT_INFO->dummy_wag)
275                trace_destroy_dead(RT_INFO->dummy_wag);
276
277        if (RT_INFO->dummy_linux)
278                trace_destroy_dead(RT_INFO->dummy_linux);
279
280        close(RT_INFO->input_fd);
281        free(libtrace->format_data);
282        return 0;
283}
284
285#define RT_BUF_SIZE 4000
286
287static int rt_read(libtrace_t *libtrace, void **buffer, size_t len, int block) {
288        int numbytes;
289        rt_header_t *test_hdr;
290       
291        assert(len <= RT_BUF_SIZE);
292       
293        if (!RT_INFO->pkt_buffer) {
294                RT_INFO->pkt_buffer = malloc(RT_BUF_SIZE);
295                RT_INFO->buf_current = RT_INFO->pkt_buffer;
296                RT_INFO->buf_filled = 0;
297        }
298
299#ifndef MSG_DONTWAIT
300#define MSG_DONTWAIT 0
301#endif
302
303        if (block)
304                block=0;
305        else
306                block=MSG_DONTWAIT;
307
308       
309        if (len > RT_INFO->buf_filled) {
310                memcpy(RT_INFO->pkt_buffer, RT_INFO->buf_current, 
311                                RT_INFO->buf_filled);
312                RT_INFO->buf_current = RT_INFO->pkt_buffer;
313               
314#ifndef MSG_NOSIGNAL
315#  define MSG_NOSIGNAL 0
316#endif
317                while (len > RT_INFO->buf_filled) {
318                        if ((numbytes = recv(RT_INFO->input_fd,
319                                                RT_INFO->buf_current + 
320                                                RT_INFO->buf_filled,
321                                                RT_BUF_SIZE-RT_INFO->buf_filled,
322                                                MSG_NOSIGNAL|block)) <= 0) {
323                                if (numbytes == 0) {
324                                        trace_set_err(libtrace, TRACE_ERR_BAD_PACKET, 
325                                                        "No data received");
326                                        return -1;
327                                }
328                               
329                                if (errno == EINTR) {
330                                        /* ignore EINTR in case
331                                         * a caller is using signals
332                                         */
333                                        continue;
334                                }
335                                if (errno == EAGAIN) {
336                                        trace_set_err(libtrace,
337                                                        EAGAIN,
338                                                        "EAGAIN");
339                                        return -1;
340                                }
341                               
342                                perror("recv");
343                                trace_set_err(libtrace, errno,
344                                                "Failed to read data into rt recv buffer");
345                                return -1;
346                        }
347                        /*
348                        buf_ptr = RT_INFO->pkt_buffer;
349                        for (i = 0; i < RT_BUF_SIZE ; i++) {
350                                       
351                                printf("%02x", (unsigned char)*buf_ptr);
352                                buf_ptr ++;
353                        }
354                        printf("\n");
355                        */
356                        RT_INFO->buf_filled+=numbytes;
357                }
358
359        }
360        *buffer = RT_INFO->buf_current;
361        RT_INFO->buf_current += len;
362        RT_INFO->buf_filled -= len;
363        assert(RT_INFO->buf_filled >= 0);
364        return len;
365}
366
367
368static int rt_set_format(libtrace_t *libtrace, libtrace_packet_t *packet) 
369{
370       
371        if (packet->type >= RT_DATA_PCAP) {
372                if (!RT_INFO->dummy_pcap) {
373                        RT_INFO->dummy_pcap = trace_create_dead("pcap:-");
374                }
375                packet->trace = RT_INFO->dummy_pcap;
376                return 0;       
377        }
378
379        switch (packet->type) {
380                case RT_DUCK_2_4:
381                case RT_DUCK_2_5:
382                        if (!RT_INFO->dummy_duck) {
383                                RT_INFO->dummy_duck = trace_create_dead("duck:dummy");
384                        }
385                        packet->trace = RT_INFO->dummy_duck;
386                        break;
387                case RT_DATA_ERF:
388                        if (!RT_INFO->dummy_erf) {
389                                RT_INFO->dummy_erf = trace_create_dead("erf:-");
390                        }
391                        packet->trace = RT_INFO->dummy_erf;
392                        break;
393                case RT_DATA_WAG:
394                        if (!RT_INFO->dummy_wag) {
395                                RT_INFO->dummy_wag = trace_create_dead("wtf:-");
396                        }
397                        packet->trace = RT_INFO->dummy_wag;
398                        break;
399                case RT_DATA_LINUX_NATIVE:
400                        if (!RT_INFO->dummy_linux) {
401                                RT_INFO->dummy_linux = trace_create_dead("int:");
402                        }
403                        packet->trace = RT_INFO->dummy_linux;
404                        break;
405                case RT_DATA_LEGACY_ETH:
406                case RT_DATA_LEGACY_ATM:
407                case RT_DATA_LEGACY_POS:
408                        printf("Sending legacy over RT is currently not supported\n");
409                        trace_set_err(libtrace, TRACE_ERR_BAD_PACKET, "Legacy packet cannot be sent over rt");
410                        return -1;
411                default:
412                        printf("Unrecognised format: %d\n", packet->type);
413                        trace_set_err(libtrace, TRACE_ERR_BAD_PACKET, "Unrecognised packet format");
414                        return -1;
415        }
416        return 0; /* success */
417}               
418
419static void rt_set_payload(libtrace_packet_t *packet) {
420        dag_record_t *erfptr;
421       
422        switch (packet->type) {
423                case RT_DATA_ERF:
424                        erfptr = (dag_record_t *)packet->header;
425                       
426                        if (erfptr->flags.rxerror == 1) {
427                                packet->payload = NULL;
428                                break;
429                        }
430                        /* else drop into the default case */
431                default:
432                        packet->payload = (char *)packet->buffer +
433                                trace_get_framing_length(packet);
434                        break;
435        }
436}
437
438static int rt_send_ack(libtrace_t *libtrace, 
439                uint32_t seqno)  {
440       
441        static char *ack_buffer = 0;
442        char *buf_ptr;
443        int numbytes = 0;
444        int to_write = 0;
445        rt_header_t *hdr;
446        rt_ack_t *ack_hdr;
447       
448        if (!ack_buffer) {
449                ack_buffer = malloc(sizeof(rt_header_t) + sizeof(rt_ack_t));
450        }
451       
452        hdr = (rt_header_t *) ack_buffer;
453        ack_hdr = (rt_ack_t *) (ack_buffer + sizeof(rt_header_t));
454       
455        hdr->type = RT_ACK;
456        hdr->length = sizeof(rt_ack_t);
457
458        ack_hdr->sequence = seqno;
459       
460        to_write = hdr->length + sizeof(rt_header_t);
461        buf_ptr = ack_buffer;
462
463        while (to_write > 0) {
464                numbytes = send(RT_INFO->input_fd, buf_ptr, to_write, 0); 
465                if (numbytes == -1) {
466                        if (errno == EINTR || errno == EAGAIN) {
467                                continue;
468                        }
469                        else {
470                                printf("Error sending ack\n");
471                                trace_set_err(libtrace, TRACE_ERR_BAD_PACKET, 
472                                                "Error sending ack");
473                                return -1;
474                        }
475                }
476                to_write = to_write - numbytes;
477                buf_ptr = buf_ptr + to_write;
478               
479        }
480
481        return 1;
482}
483
484       
485static int rt_read_packet_versatile(libtrace_t *libtrace,
486                libtrace_packet_t *packet,int blocking) {
487        static rt_header_t rt_hdr;
488        static rt_header_t *pkt_hdr = 0;
489       
490        if (pkt_hdr == 0) {
491                /* first time through */
492                pkt_hdr = malloc(sizeof(rt_header_t));
493                rt_hdr.type = RT_LAST;
494        }
495       
496        if (packet->buf_control == TRACE_CTRL_EXTERNAL || !packet->buffer) {
497                packet->buf_control = TRACE_CTRL_PACKET;
498                packet->buffer = malloc(LIBTRACE_PACKET_BUFSIZE);
499        } 
500
501        /* RT_LAST means that the next bytes received should be a
502         * rt header - I know it's hax and maybe I'll fix it later on */
503        if (rt_hdr.type == RT_LAST) {
504       
505                /* FIXME: Better error handling required */
506                if (rt_read(libtrace, (void **)&pkt_hdr, 
507                                sizeof(rt_header_t),blocking) !=
508                                sizeof(rt_header_t)) {
509                        return -1;
510                }
511
512                /* Need to salvage these in case the next rt_read overwrites
513                 * the buffer they came from! */
514                rt_hdr.type = pkt_hdr->type;
515                rt_hdr.length = pkt_hdr->length;
516                rt_hdr.sequence = pkt_hdr->sequence;
517        }
518        packet->type = rt_hdr.type;
519        packet->size = rt_hdr.length;
520
521        if (packet->type >= RT_DATA_SIMPLE) {
522                if (rt_read(libtrace, &packet->buffer, rt_hdr.length,blocking) != rt_hdr.length) {
523                        return -1;
524                }
525                packet->header = packet->buffer;
526               
527                if (RT_INFO->reliable > 0) {
528                        if (rt_send_ack(libtrace, rt_hdr.sequence) 
529                                        == -1)
530                        {
531                                return -1;
532                        }
533                }
534               
535                       
536                if (rt_set_format(libtrace, packet) < 0) {
537                        return -1;
538                }
539                rt_set_payload(packet);
540        } else {
541                switch(packet->type) {
542                        case RT_STATUS:
543                                if (rt_read(libtrace, &packet->buffer, 
544                                        rt_hdr.length, blocking) != 
545                                                rt_hdr.length) {
546                                        return -1;
547                                }
548                                packet->header = 0;
549                                packet->payload = packet->buffer;
550                                break;
551                        case RT_DUCK_2_4:
552                        case RT_DUCK_2_5:
553                                if (rt_read(libtrace, &packet->buffer,
554                                        rt_hdr.length, blocking) != 
555                                                rt_hdr.length) {
556                                        return -1;
557                                }
558                                if (rt_set_format(libtrace, packet) < 0) {
559                                        return -1;
560                                }
561                                packet->header = 0;
562                                packet->payload = packet->buffer;
563                                break;
564                        case RT_END_DATA:
565                                return 0;
566                        case RT_PAUSE_ACK:
567                                /* FIXME: Do something useful */
568                                break;
569                        case RT_OPTION:
570                                /* FIXME: Do something useful here as well */
571                                break;
572                        case RT_KEYCHANGE:
573                                break;
574                        case RT_LOSTCONN:
575                                break;
576                        default:
577                                printf("Bad rt type for client receipt: %d\n",
578                                        packet->type);
579                                return -1;
580                }
581        }
582        /* Return the number of bytes read from the stream */
583        rt_hdr.type = RT_LAST;
584        return packet->size; 
585}
586
587static int rt_read_packet(libtrace_t *libtrace,
588                libtrace_packet_t *packet) {
589        return rt_read_packet_versatile(libtrace,packet,1);
590}
591
592
593static int rt_get_capture_length(const libtrace_packet_t *packet) {
594        switch (packet->type) {
595                case RT_STATUS:
596                        return sizeof(rt_status_t);
597                case RT_HELLO:
598                        return sizeof(rt_hello_t);
599                case RT_START:
600                        return 0;
601                case RT_ACK:
602                        return sizeof(rt_ack_t);
603                case RT_END_DATA:
604                        return 0;
605                case RT_CLOSE:
606                        return 0;
607                case RT_DENY_CONN:
608                        return sizeof(rt_deny_conn_t);
609                case RT_PAUSE:
610                        return 0; 
611                case RT_PAUSE_ACK:
612                        return 0;
613                case RT_OPTION:
614                        return 0; /* FIXME */
615                case RT_KEYCHANGE:
616                        return 0;
617        }
618        printf("Unknown type: %d\n", packet->type);
619        return 0;
620}
621
622static int rt_get_wire_length(const libtrace_packet_t *packet) {
623        return 0;
624}
625                       
626static int rt_get_framing_length(const libtrace_packet_t *packet) {
627        return 0;
628}
629
630static int rt_get_fd(const libtrace_t *trace) {
631        return ((struct rt_format_data_t *)trace->format_data)->input_fd;
632}
633
634libtrace_eventobj_t trace_event_rt(libtrace_t *trace, libtrace_packet_t *packet) {
635        libtrace_eventobj_t event = {0,0,0.0,0};
636        libtrace_err_t read_err;
637
638        assert(trace);
639        assert(packet);
640       
641        if (trace->format->get_fd) {
642                event.fd = trace->format->get_fd(trace);
643        } else {
644                event.fd = 0;
645        }
646
647        event.size = rt_read_packet_versatile(trace, packet, 0);
648        if (event.size == -1) {
649                read_err = trace_get_err(trace);
650                if (read_err.err_num == EAGAIN) {
651                        event.type = TRACE_EVENT_IOWAIT;
652                }
653                else {
654                        //printf("packet error\n");
655                        event.type = TRACE_EVENT_PACKET;
656                }
657        } else if (event.size == 0) {
658                event.type = TRACE_EVENT_TERMINATE;
659               
660        }       
661        else {
662                event.type = TRACE_EVENT_PACKET;
663        }
664       
665        return event;
666}
667
668static void rt_help() {
669        printf("rt format module\n");
670        printf("Supported input URIs:\n");
671        printf("\trt:hostname:port\n");
672        printf("\trt:hostname (connects on default port)\n");
673        printf("\n");
674        printf("\te.g.: rt:localhost\n");
675        printf("\te.g.: rt:localhost:32500\n");
676        printf("\n");
677
678}
679
680
681static struct libtrace_format_t rt = {
682        "rt",
683        "$Id$",
684        TRACE_FORMAT_RT,
685        rt_init_input,                  /* init_input */
686        NULL,                           /* config_input */
687        rt_start_input,                 /* start_input */
688        rt_pause_input,                 /* pause */
689        NULL,                           /* init_output */
690        NULL,                           /* config_output */
691        NULL,                           /* start_output */
692        rt_fin_input,                   /* fin_input */
693        NULL,                           /* fin_output */
694        rt_read_packet,                 /* read_packet */
695        NULL,                           /* fin_packet */
696        NULL,                           /* write_packet */
697        NULL,                           /* get_link_type */
698        NULL,                           /* get_direction */
699        NULL,                           /* set_direction */
700        NULL,                           /* get_erf_timestamp */
701        NULL,                           /* get_timeval */
702        NULL,                           /* get_seconds */
703        NULL,                           /* seek_erf */
704        NULL,                           /* seek_timeval */
705        NULL,                           /* seek_seconds */
706        rt_get_capture_length,          /* get_capture_length */
707        rt_get_wire_length,                     /* get_wire_length */
708        rt_get_framing_length,          /* get_framing_length */
709        NULL,                           /* set_capture_length */
710        rt_get_fd,                      /* get_fd */
711        trace_event_rt,             /* trace_event */
712        rt_help,                        /* help */
713        NULL                            /* next pointer */
714};
715
716void rt_constructor() {
717        register_format(&rt);
718}
Note: See TracBrowser for help on using the repository browser.