altos/test: Adjust CRC error rate after FEC fix
[fw/altos] / map-server / altos-map / altos-map.c
1 /*
2  * Copyright © 2018 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 #include <jansson.h>
16 #include <stdlib.h>
17 #include <stdarg.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/socket.h>
28 #include <netinet/ip.h>
29
30 #define ALTOS_MAP_PORT  16717
31
32 #define MISSING         0x7fffffff
33
34 #define ALTOS_MAP_PROTOCOL_VERSION      "1.0.0"
35
36 static char *
37 reason_string(int code)
38 {
39         switch (code) {
40         case 200:
41                 return "OK";
42         case 400:
43                 return "Bad Request";
44         case 403:
45                 return "Forbidden";
46         case 404:
47                 return "Not Found";
48         case 408:
49                 return "Request Timeout";
50         default:
51                 return "Failure";
52         }
53 }
54
55 static void
56 write_status(int status)
57 {
58         printf("Status: %d %s\n", status, reason_string(status));
59 }
60
61 static void
62 write_type(char * type)
63 {
64         printf("Content-Type: %s\n", type);
65 }
66
67 static void
68 fail(int status, char *format, ...)
69 {
70         va_list ap;
71
72         write_status(status);
73         write_type("text/html");
74         printf("\n");
75         printf("<html>\n");
76         printf("<head><title>Map Fetch Failure</title></head>\n");
77         printf("<body>\n");
78         va_start(ap, format);
79         vprintf(format, ap);
80         va_end(ap);
81         printf("</body>\n");
82         printf("</html>\n");
83         exit(1);
84 }
85
86 static char *
87 getenv_copy(const char *name)
88 {
89         const char *value = getenv(name);
90
91         if (!value)
92                 return NULL;
93
94         return strdup(value);
95 }
96
97 static double
98 parse_double(char *string)
99 {
100         char *end;
101         double value;
102
103         value = strtod(string, &end);
104         if (*end)
105                 fail(400, "Invalid double %s", string);
106         return value;
107 }
108
109 static int
110 parse_int(char *string)
111 {
112         char *end;
113         long int value;
114
115         value = strtol(string, &end, 10);
116         if (*end)
117                 fail(400, "Invalid int %s", string);
118         if (value < INT_MIN || INT_MAX < value)
119                 fail(400, "Int value out of range %ld", value);
120         return (int) value;
121 }
122
123
124 static int
125 connect_service(void)
126 {
127         struct sockaddr_in      altos_map_addr = {
128                 .sin_family = AF_INET,
129                 .sin_port = htons(ALTOS_MAP_PORT),
130                 .sin_addr = {
131                         .s_addr = htonl(INADDR_LOOPBACK),
132                 },
133         };
134
135         int     s = socket(AF_INET, SOCK_STREAM, 0);
136
137         if (s < 0)
138                 return -1;
139
140         if (connect (s, (const struct sockaddr *) &altos_map_addr, sizeof (altos_map_addr)) < 0) {
141                 close (s);
142                 return -1;
143         }
144
145         return s;
146 }
147
148 int main(int argc, char **argv)
149 {
150
151         char *query_string = getenv_copy("QUERY_STRING");
152
153         if (query_string == NULL)
154                 fail(400, "%s", "Missing query string");
155
156         char *remote_addr = getenv_copy("REMOTE_ADDR");
157
158         if (remote_addr == NULL)
159                 fail(400, "%s", "Missing remote address");
160
161         double  lon = MISSING;
162         double  lat = MISSING;
163         int     zoom = MISSING;
164         char    *version = NULL;
165
166         char    *query, *query_save = NULL;
167         char    *query_start = query_string;
168
169         while ((query = strtok_r(query_start, "&", &query_save)) != NULL) {
170                 query_start = NULL;
171
172                 char    *token, *token_save = NULL;
173                 char    *token_start = query;
174
175                 char    *name = NULL;
176                 char    *value = NULL;
177
178                 while ((token = strtok_r(token_start, "=", &token_save)) != NULL) {
179                         token_start = NULL;
180                         if (name == NULL)
181                                 name = token;
182                         else if (value == NULL)
183                                 value = token;
184                         else
185                                 break;
186                 }
187
188                 if (name && value) {
189                         if (!strcmp(name, "lon"))
190                                 lon = parse_double(value);
191                         else if (!strcmp(name, "lat"))
192                                 lat = parse_double(value);
193                         else if (!strcmp(name, "zoom"))
194                                 zoom = parse_int(value);
195                         else if (!strcmp(name, "version"))
196                                 version = value;
197                         else
198                                 fail(400, "Extra query param \"%s\"", query);
199                 }
200         }
201
202         if (version != NULL) {
203                 printf("Content-Type: text/plain\n");
204                 printf("\n");
205                 printf("%s\n", ALTOS_MAP_PROTOCOL_VERSION);
206                 return 0;
207         }
208         if (lon == MISSING)
209                 fail(400, "Missing longitude");
210         if (lat == MISSING)
211                 fail(400, "Missing latitude");
212         if (zoom == MISSING)
213                 fail(400, "Missing zoom");
214
215         int     s = -1;
216         int     tries = 0;
217
218         while (tries < 10 && s < 0) {
219                 s = connect_service();
220                 if (s < 0) {
221                         usleep(100 * 1000);
222                         tries++;
223                 }
224         }
225
226         if (s < 0)
227                 fail(408, "Cannot connect AltOS map daemon");
228
229         FILE    *sf = fdopen(s, "r+");
230
231         if (sf == NULL)
232                 fail(400, "allocation failure");
233
234         json_t *request = json_pack("{s:f s:f s:i s:s}", "lat", lat, "lon", lon, "zoom", zoom, "remote_addr", remote_addr);
235
236         if (request == NULL)
237                 fail(400, "Cannot create JSON request");
238
239         if (json_dumpf(request, sf, 0) < 0)
240                 fail(400, "Cannot write JSON request");
241
242         fflush(sf);
243
244         json_error_t    error;
245         json_t          *reply = json_loadf(sf, 0, &error);
246
247         if (!reply)
248                 fail(400, "Cannot read JSON reply");
249
250         int     status;
251
252         if (json_unpack(reply, "{s:i}", "status", &status) < 0)
253                 fail(400, "No status returned");
254
255         if (status != 200)
256                 fail(status, "Bad cache status");
257
258         char    *filename, *content_type;
259
260         if (json_unpack(reply, "{s:s s:s}", "filename", &filename, "content_type", &content_type) < 0)
261                 fail(400, "JSON reply parse failure");
262
263         int     fd = open(filename, O_RDONLY);
264
265         if (fd < 0)
266                 fail(400, "%s: %s", filename, strerror(errno));
267
268         struct stat     statb;
269
270         if (fstat(fd, &statb) < 0)
271                 fail(400, "%s: %s", filename, strerror(errno));
272
273         printf("Content-Type: %s\n", content_type);
274         printf("Content-Length: %lu\n", (unsigned long) statb.st_size);
275         printf("\n");
276         fflush(stdout);
277
278         char    buf[4096];
279         ssize_t bytes_read;
280
281         while ((bytes_read = read(fd, buf, sizeof (buf))) > 0) {
282                 ssize_t total_write = 0;
283                 while (total_write < bytes_read) {
284                         ssize_t bytes_write = write(1, buf + total_write, bytes_read - total_write);
285                         if (bytes_write <= 0)
286                                 return 1;
287                         total_write += bytes_write;
288                 }
289         }
290         if (bytes_read < 0)
291                 return 1;
292         return 0;
293 }