diff options
-rw-r--r-- | .gitignore | 10 | ||||
-rw-r--r-- | Makefile | 18 | ||||
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | config.def.h | 12 | ||||
-rw-r--r-- | html.h | 99 | ||||
-rw-r--r-- | logo.png | bin | 0 -> 3667 bytes | |||
-rw-r--r-- | logo.svg | 98 | ||||
-rw-r--r-- | pacredir.c | 171 | ||||
-rw-r--r-- | pacredir.h | 8 |
9 files changed, 372 insertions, 51 deletions
@@ -1,10 +1,12 @@ *~ *.html *.o -pacredir -config.h compat/pacserve-announce.service -systemd/pacserve.service -version.h +config.h +favicon.h +favicon.png +pacredir pacredir-*.tar.xz pacredir-*.tar.xz.asc +systemd/pacserve.service +version.h @@ -27,7 +27,7 @@ ID := $(shell shopt -u extglob && source /etc/os-release && echo $$ID) # this is just a fallback in case you do not use git but downloaded # a release tarball... -VERSION := 0.6.0 +VERSION := 0.7.2 SERVICESIN = $(wildcard */*.service.in) SERVICES = $(SERVICESIN:.in=) @@ -36,14 +36,24 @@ HTML = $(MARKDOWN:.md=.html) all: pacredir $(SERVICES) $(HTML) -pacredir: pacredir.c pacredir.h config.h version.h +pacredir: pacredir.c pacredir.h config.h favicon.h html.h version.h $(CC) $< $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -DREPRODUCIBLE=$(REPRODUCIBLE) -o $@ config.h: config.def.h $(CP) $< $@ version.h: $(wildcard .git/HEAD .git/index .git/refs/tags/*) Makefile - printf "#ifndef VERSION\n#define VERSION \"%s\"\n#endif\n#define ARCH \"%s\"\n#define ID \"%s\"\n" $(shell git describe --long 2>/dev/null || echo ${VERSION}) $(ARCH) $(ID) > $@ + printf '#ifndef VERSION_H\n#define VERSION_H\n#define VERSION\t"%s"\n#define ARCH\t"%s"\n#define ID\t"%s"\n#endif\n' $(shell git describe --long 2>/dev/null || echo ${VERSION}) $(ARCH) $(ID) > $@ + +favicon.png: logo.svg Makefile + rsvg-convert --width 32 --height 32 $< > $@ + oxipng $@ + +favicon.h: favicon.png Makefile + printf '#ifndef FAVICON_H\n#define FAVICON_H\n' > $@ + printf 'static unsigned char favicon[] = {\n' >> $@ + od -t x1 -A n -v < $< | sed 's/\([0-9a-f]\{2\}\)/0x\1,/g' >> $@ + printf '};\n#define FAVICON_SHA1 "%s"\n#endif\n' $(shell sha1sum $< | cut -d' ' -f1) >> $@ %.service: %.service.in $(SED) 's/%ARCH%/$(ARCH)/; s/%ARCH_BYTES%/$(shell (printf $(ARCH) | wc -c; printf $(ARCH) | od -t d1 -A n) | tr -s " ")/; s/%ID%/$(ID)/; s/%ID_BYTES%/$(shell (printf $(ID) | wc -c; printf $(ID) | od -t d1 -A n) | tr -s " ")/' $< > $@ @@ -79,7 +89,7 @@ install-avahi: compat/pacserve-announce.service $(INSTALL) -D -m0644 compat/02-pacredir-avahi-MulticastDNS-resolve.conf $(DESTDIR)/etc/systemd/resolved.conf.d/02-pacredir-avahi-MulticastDNS-resolve.conf clean: - $(RM) -f *.o *~ pacredir $(SERVICES) $(HTML) version.h + $(RM) -f *.o *~ pacredir $(SERVICES) $(HTML) favicon.png favicon.h version.h distclean: $(RM) -f *.o *~ pacredir $(SERVICES) $(HTML) version.h config.h @@ -7,6 +7,8 @@ pacredir **pacredir - redirect pacman requests, assisted by mDNS Service Discovery** + + By default every [Arch Linux ↗️](https://archlinux.org/) installation downloads its package files from online mirrors, transferring all the bits via WAN connection. @@ -80,6 +82,11 @@ repository definitions in `pacman.conf`: To get a better idea what happens in the background have a look at [the request flow chart](FLOW.md). +### Status page + +A simple status page is available when `pacredir` is running. Just point +your browser to [your local instance](http://localhost:7077/). + ### Databases from cache server By default databases are not fetched from cache servers. To make that diff --git a/config.def.h b/config.def.h index fef2e1c..b2e29fe 100644 --- a/config.def.h +++ b/config.def.h @@ -23,15 +23,9 @@ #define DROP_PRIV_UID 65534 #define DROP_PRIV_GID 65534 -/* website url */ -#define WEBSITE "https://pacredir.eworm.de/" - -/* This is used for default documents. Usually you will not see this anyway. */ -#define PAGE307 "<html><head><title>307 temporary redirect</title>" \ - "</head><body>307 temporary redirect: " \ - "<a href=\"%s\">%s</a></body></html>" -#define PAGE404 "<html><head><title>404 Not Found</title>" \ - "</head><body>404 Not Found: %s</body></html>" +/* website & url */ +#define WEBSITE "pacredir.eworm.de" +#define WEBURL "https://" WEBSITE "/" /* the ports pacredir and pacserve listen to */ #define PORT_PACREDIR 7077 @@ -0,0 +1,99 @@ +/* + * (C) 2013-2025 by Christian Hesse <mail@eworm.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#ifndef _HTML_H +#define _HTML_H + +/* This is used for default documents. Usually you will not see this anyway. */ +#define PAGE307 \ + "<html><head><title>307 temporary redirect</title>" \ + "</head><body>307 temporary redirect: " \ + "<a href=\"%s\">%s</a></body></html>" +#define PAGE404 \ + "<html><head><title>404 Not Found</title>" \ + "</head><body>404 Not Found: %s</body></html>" + +/* status page */ +#define CIRCLE_GREEN "🟢" +#define CIRCLE_YELLOW "🟡" +#define CIRCLE_ORANGE "🟠" +#define CIRCLE_RED "🔴" +#define CIRCLE_BLUE "🔵" + +#define STATUS_HEAD \ + "<!DOCTYPE html><html lang=\"en\">" \ + "<head><title>pacredir status</title>" \ + "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">" \ + "<meta http-equiv=\"refresh\" content=\"30\">" \ + "<style>body { font-family: sans-serif; } " \ + "h1 { padding-left: 36px; background-image: url(\"/favicon.png\"); background-repeat: no-repeat; } " \ + "a { color: black; text-decoration: none; } " \ + "a:hover { text-decoration: underline; } " \ + "th { background: #efefef; } " \ + "td { text-align: center; padding: 1px 5px; } " \ + "tr:nth-child(even) { background: #dfdfdf; } " \ + "tr:nth-child(odd) { background: #efefef; } " \ + "tr:hover { background: #dfefef; }</style>" \ + "<link rel=\"icon\" href=\"/favicon.png \" type=\"image/png\">" \ + "</head><body><h1>pacredir status</h1>" \ + "<p>This is <code>pacredir</code> version <i>" VERSION "</i> running on localhost. " \ + "Visit <a href=\"" WEBURL "\">" WEBSITE "</a> for documentation.</p>" \ + "<table>" \ + "<tr><td>Distribution:</td><td><b>" ID "</b></td></tr>" \ + "<tr><td>Architecture:</td><td><b>" ARCH "</b></td></tr>" \ + "<tr><td>Redirects:</td><td><b>%d</b></td></tr>" \ + "<tr><td>Not found:</td><td><b>%d</b></td></tr>" \ + "<tr><td>Over all:</td><td><b>%s</b></td></tr>" \ + "</table>" + +#define STATUS_INT_HEAD \ + "<h2 id=\"ignored-interfaces\"><a href=\"#ignored-interfaces\">Ignored interfaces</a></h2>" \ + "<table><tr><th>interface</th><th>link</th></tr>" +#define STATUS_INT_ONE \ + "<tr><td>%s</td><td>%d</td></tr>" +#define STATUS_INT_ONE_NA \ + "<tr><td>%s</td><td>" CIRCLE_ORANGE "</td></tr>" +#define STATUS_INT_NONE \ + "<tr><td colspan=2>(none)</td></tr>" +#define STATUS_INT_FOOT \ + "</table>" + +#define STATUS_HOST_HEAD \ + "<h2 id=\"hosts\"><a href=\"#hosts\">Hosts</a></h2>" \ + "<table><tr>" \ + "<th>host</th>" \ + "<th>port</th>" \ + "<th colspan=2>state</th>" \ + "<th colspan=2>finds</th>" \ + "<th colspan=2>bad</th></tr>" +#define STATUS_HOST_ONE \ + "<tr>" \ + "<td>%s</td>" \ + "<td>%d</td>" \ + "<td>%s</td><td>%s</td>" \ + "<td>%s</td><td>%d</td>" \ + "<td>%s</td><td>%d</td></tr>" +#define STATUS_HOST_NONE \ + "<tr><td colspan=9>(none)</td></tr>" +#define STATUS_HOST_FOOT \ + "</table>" + +#define STATUS_FOOT \ + "</body></html>" + +#endif /* _HTML_H */ diff --git a/logo.png b/logo.png Binary files differnew file mode 100644 index 0000000..5dad40f --- /dev/null +++ b/logo.png diff --git a/logo.svg b/logo.svg new file mode 100644 index 0000000..6626c12 --- /dev/null +++ b/logo.svg @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> + +<svg + fill="#000000" + width="96" + height="96" + viewBox="0 0 2.88 2.88" + version="1.1" + id="svg1" + sodipodi:docname="logo.svg" + inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)" + inkscape:export-filename="pacman-redir.png" + inkscape:export-xdpi="192" + inkscape:export-ydpi="192" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs1" /> + <sodipodi:namedview + id="namedview1" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + showgrid="false" + inkscape:zoom="8.8561223" + inkscape:cx="47.932942" + inkscape:cy="45.956908" + inkscape:window-width="1920" + inkscape:window-height="1047" + inkscape:window-x="0" + inkscape:window-y="33" + inkscape:window-maximized="1" + inkscape:current-layer="svg1" /> + <g + id="g2"> + <rect + style="fill:#ffffff;stroke-width:0.06;stroke-linecap:round;stroke-linejoin:round" + id="rect1" + x="0" + y="0" + height="2.8800001" + width="2.8800001" + rx="0.45000005" + ry="0.45000005" /> + <path + style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:0.18;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" + id="path2" + sodipodi:type="arc" + sodipodi:cx="1.35" + sodipodi:cy="1.3500063" + sodipodi:rx="1.17" + sodipodi:ry="1.17" + sodipodi:start="0.61086524" + sodipodi:end="5.6723201" + sodipodi:arc-type="slice" + d="M 2.3084079,2.0210907 A 1.17,1.17 0 0 1 0.99817424,2.4658551 1.17,1.17 0 0 1 0.18000007,1.3500063 1.17,1.17 0 0 1 0.99817428,0.23415754 1.17,1.17 0 0 1 2.3084079,0.67892197 L 1.35,1.3500063 Z" /> + <circle + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06;stroke-linecap:round;stroke-linejoin:round" + id="path3" + cx="1.08" + cy="0.81000006" + r="0.27000001" /> + <g + id="g1"> + <rect + style="fill:#dd55ff;fill-opacity:1;stroke:#000000;stroke-width:0.18;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none" + id="rect4" + width="1.2335274" + height="0.60436016" + x="1.4664726" + y="1.8063096" + ry="0.30218008" /> + <path + sodipodi:type="star" + style="fill:#dd55ff;fill-opacity:1;stroke:#000000;stroke-width:0.06;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none" + id="path4" + inkscape:flatsided="true" + sodipodi:sides="3" + sodipodi:cx="-0.16725793" + sodipodi:cy="0.41981158" + sodipodi:r1="0.22600283" + sodipodi:r2="0.2553075" + sodipodi:arg1="1.017775" + sodipodi:arg2="2.0649725" + inkscape:rounded="0.1" + inkscape:randomized="0" + d="m -0.04854756,0.61212674 c -0.03330996,0.0205612 -0.34346381,-0.14653865 -0.34461539,-0.18566654 -0.001152,-0.0391279 0.29863811,-0.22417907 0.33309965,-0.20561241 0.03446154,0.0185667 0.04482571,0.37071771 0.01151574,0.39127895 z" + transform="matrix(3.0000001,0,0,3.0000001,2.036195,0.85837623)" /> + </g> + </g> +</svg> @@ -38,9 +38,10 @@ unsigned int count_redirect = 0, count_not_found = 0; /*** write_log ***/ static int write_log(FILE *stream, const char *format, ...) { va_list args; - va_start(args, format); + va_start(args, format); vfprintf(stream, format, args); + va_end(args); fflush(stream); return EXIT_SUCCESS; @@ -136,7 +137,7 @@ static void update_hosts(const uint8_t refcnt) { r = sd_bus_open_system(&bus); if (r < 0) { write_log(stderr, "Failed to open system bus: %s\n", strerror(-r)); - goto finish; + goto fast_finish; } r = sd_bus_call_method(bus, "org.freedesktop.resolve1", "/org/freedesktop/resolve1", @@ -157,7 +158,7 @@ static void update_hosts(const uint8_t refcnt) { if (refcnt == 0) { usleep(250000); update_hosts(refcnt + 1); - goto finish; + goto fast_finish; } r = sd_bus_message_enter_container(reply_record, 'a', "(iqqay)"); @@ -337,6 +338,12 @@ finish_service: if (r < 0) goto parse_failure_record; + goto finish; + +parse_failure_record: + write_log(stderr, "Parse failure for record: %s\n", strerror(-r)); + +finish: /* mark hosts offline that did not show up in query */ hosts_ptr = hosts; while (hosts_ptr->host != NULL) { @@ -348,12 +355,7 @@ finish_service: hosts_ptr = hosts_ptr->next; } - goto finish; - -parse_failure_record: - write_log(stderr, "Parse failure for record: %s\n", strerror(-r)); - -finish: +fast_finish: sd_bus_message_unref(reply_record); sd_bus_flush_close_unref(bus); } @@ -382,6 +384,7 @@ static int add_host(const char * host, const uint16_t port, const uint8_t mdns) hosts_ptr->mdns = mdns; hosts_ptr->badtime = 0; hosts_ptr->badcount = 0; + hosts_ptr->finds = 0; hosts_ptr->next = malloc(sizeof(struct hosts)); hosts_ptr->next->host = NULL; @@ -489,6 +492,87 @@ static void * get_http_code(void * data) { return NULL; } +/* append_string */ +static char * append_string(char * string, const char *format, ...) { + va_list args; + size_t string_len = 0, append_len; + + if (string != NULL) + string_len = strlen(string); + + va_start(args, format); + append_len = vsnprintf(NULL, 0, format, args) + 1; + va_end(args); + + string = realloc(string, string_len + append_len); + + va_start(args, format); + vsnprintf(string + string_len, append_len, format, args); + va_end(args); + + return string; +} +/*** status_page ***/ +static char * status_page(void) { + struct ignore_interfaces * ignore_interfaces_ptr = ignore_interfaces; + struct hosts * hosts_ptr = hosts; + char *page = NULL, *overall = CIRCLE_BLUE; + + if (count_redirect + count_not_found) + switch (count_redirect * 4 / (count_redirect + count_not_found)) { + case 0: + overall = CIRCLE_RED; + break; + case 1: + overall = CIRCLE_ORANGE; + break; + case 2: + overall = CIRCLE_YELLOW; + break; + default: + overall = CIRCLE_GREEN; + break; + } + + page = append_string(page, STATUS_HEAD, count_redirect, count_not_found, overall); + + page = append_string(page, STATUS_INT_HEAD); + if (ignore_interfaces_ptr->interface == NULL) + page = append_string(page, STATUS_INT_NONE); + while (ignore_interfaces_ptr->interface != NULL) { + if (ignore_interfaces_ptr->ifindex > 0) + /* write_log(stdout, STATUS_INT_ONE, + ignore_interfaces_ptr->interface, ignore_interfaces_ptr->ifindex); */ + page = append_string(page, STATUS_INT_ONE, + ignore_interfaces_ptr->interface, ignore_interfaces_ptr->ifindex); + else + page = append_string(page, STATUS_INT_ONE_NA, + ignore_interfaces_ptr->interface); + + ignore_interfaces_ptr = ignore_interfaces_ptr->next; + } + page = append_string(page, STATUS_INT_FOOT); + + page = append_string(page, STATUS_HOST_HEAD); + if (hosts_ptr->host == NULL) + page = append_string(page, STATUS_HOST_NONE); + while (hosts_ptr->host != NULL) { + page = append_string(page, STATUS_HOST_ONE, + hosts_ptr->host, hosts_ptr->port, + hosts_ptr->mdns ? (hosts_ptr->online ? CIRCLE_GREEN : CIRCLE_RED) : CIRCLE_BLUE, + hosts_ptr->mdns ? (hosts_ptr->online ? "online" : "offline") : "static", + hosts_ptr->finds ? CIRCLE_GREEN : CIRCLE_BLUE, hosts_ptr->finds, + hosts_ptr->badcount ? CIRCLE_RED : CIRCLE_BLUE, hosts_ptr->badcount); + + hosts_ptr = hosts_ptr->next; + } + page = append_string(page, STATUS_HOST_FOOT); + + page = append_string(page, STATUS_FOOT); + + return page; +} + /*** ahc_echo *** * called whenever a http request is received */ static enum MHD_Result ahc_echo(void * cls, @@ -516,13 +600,33 @@ static enum MHD_Result ahc_echo(void * cls, pthread_t * tid = NULL; struct request ** requests = NULL; struct request * request = NULL; - long http_code = 0; + long http_code = MHD_HTTP_NOT_FOUND; double time_total = INFINITY; char ctime[26]; /* initialize struct timeval */ gettimeofday(&tv, NULL); + /* give status page */ + if (strcmp(uri, "/") == 0) { + http_code = MHD_HTTP_OK; + page = status_page(); + goto response; + } + + /* give favicon */ + if (strcmp(uri, "/favicon.png") == 0) { + http_code = MHD_HTTP_OK; + goto response; + } + + /* give a simple ok response for monitoring */ + if (strcmp(uri, "/check") == 0) { + http_code = MHD_HTTP_OK; + page = strdup("OK"); + goto response; + } + /* we want the filename, not the path */ basename = uri; while (strstr(basename, "/") != NULL) @@ -546,15 +650,6 @@ static enum MHD_Result ahc_echo(void * cls, /* clear context pointer */ *ptr = NULL; - /* redirect to website if no file given */ - if (*basename == 0) { - http_code = MHD_HTTP_OK; - /* duplicate string so we can free it later */ - url = strdup(WEBSITE); - host = basename = "project site"; - goto response; - } - /* process db file request (*.db and *.files) */ if ((strlen(basename) > 3 && strcmp(basename + strlen(basename) - 3, ".db") == 0) || (strlen(basename) > 6 && strcmp(basename + strlen(basename) - 6, ".files") == 0)) { @@ -663,11 +758,12 @@ static enum MHD_Result ahc_echo(void * cls, request->time_total < time_total))) || /* for packages try to guess the fastest peer */ (dbfile == 0 && request->time_total < time_total))) { + request->host->finds++; if (url != NULL) free(url); url = request->url; host = request->host->host; - http_code = MHD_HTTP_OK; + http_code = MHD_HTTP_TEMPORARY_REDIRECT; last_modified = request->last_modified; time_total = request->time_total; } else @@ -677,22 +773,33 @@ static enum MHD_Result ahc_echo(void * cls, /* increase counters before reponse label, do not count redirects to project page */ - if (http_code == MHD_HTTP_OK) + if (http_code == MHD_HTTP_TEMPORARY_REDIRECT) count_redirect++; else count_not_found++; response: /* give response */ - if (http_code == MHD_HTTP_OK) { + if (http_code == MHD_HTTP_TEMPORARY_REDIRECT) { write_log(stdout, "Redirecting to %s: %s\n", host, url); page = malloc(strlen(PAGE307) + strlen(url) + strlen(basename) + 1); sprintf(page, PAGE307, url, basename); response = MHD_create_response_from_buffer(strlen(page), (void*) page, MHD_RESPMEM_MUST_FREE); ret = MHD_add_response_header(response, "Location", url); - ret = MHD_queue_response(connection, MHD_HTTP_TEMPORARY_REDIRECT, response); free(url); - } else { + } else if (http_code == MHD_HTTP_OK) { + if (page != NULL) { + write_log(stdout, "Sending status page.\n"); + response = MHD_create_response_from_buffer(strlen(page), (void*) page, MHD_RESPMEM_MUST_FREE); + ret = MHD_add_response_header (response, "Content-Type", "text/html"); + } else { + write_log(stdout, "Sending favicon.\n"); + response = MHD_create_response_from_buffer(sizeof(favicon), favicon, MHD_RESPMEM_PERSISTENT); + ret = MHD_add_response_header(response, "ETag", FAVICON_SHA1); + ret = MHD_add_response_header(response, "Cache-Control", "max-age=86400"); + ret = MHD_add_response_header (response, "Content-Type", "image/png"); + } + } else { /* MHD_HTTP_NOT_FOUND */ if (req_count < 0) write_log(stdout, "Currently no peers are available to check for %s.\n", basename); @@ -706,9 +813,10 @@ response: page = malloc(strlen(PAGE404) + strlen(basename) + 1); sprintf(page, PAGE404, basename); response = MHD_create_response_from_buffer(strlen(page), (void*) page, MHD_RESPMEM_MUST_FREE); - ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); } + ret = MHD_add_response_header(response, "Server", PROGNAME " v" VERSION " " ID "/" ARCH); + ret = MHD_queue_response(connection, http_code, response); MHD_destroy_response(response); /* report counts to systemd */ @@ -770,15 +878,10 @@ static void sigusr_callback(int signal) { if (hosts_ptr->host == NULL) write_log(stdout, " (none)\n"); while (hosts_ptr->host != NULL) { - if (hosts_ptr->badcount > 0) - write_log(stdout, " -> %s (%s, %s, port: %d, bad count: %d)\n", - hosts_ptr->host, hosts_ptr->mdns ? "mdns" : "static", - hosts_ptr->online ? "online" : "offline", hosts_ptr->port, - hosts_ptr->badcount); - else - write_log(stdout, " -> %s (%s, %s, port: %d)\n", - hosts_ptr->host, hosts_ptr->mdns ? "mdns" : "static", - hosts_ptr->online ? "online" : "offline", hosts_ptr->port); + write_log(stdout, " -> %s (%s, %s, port: %d, finds: %d, bad: %d)\n", + hosts_ptr->host, hosts_ptr->mdns ? "mdns" : "static", + hosts_ptr->online ? "online" : "offline", hosts_ptr->port, + hosts_ptr->finds, hosts_ptr->badcount); hosts_ptr = hosts_ptr->next; } @@ -47,6 +47,8 @@ /* compile time configuration */ #include "config.h" #include "version.h" +#include "html.h" +#include "favicon.h" #define DNS_CLASS_IN 1U #define DNS_TYPE_PTR 12U @@ -76,6 +78,8 @@ struct hosts { __time_t badtime; /* count the number of bad requests */ unsigned int badcount; + /* count finds */ + unsigned int finds; /* pointer to next struct element */ struct hosts * next; }; @@ -125,6 +129,10 @@ static int add_host(const char * host, const uint16_t port, const uint8_t mdns); /* get_http_code */ static void * get_http_code(void * data); +/* append_string */ +static char * append_string(char * string, const char *format, ...); +/* status_page */ +static char * status_page(void); /* ahc_echo */ static enum MHD_Result ahc_echo(void * cls, struct MHD_Connection * connection, |