1 /******************************************************************
2 *
3 *
4 * Copyright (C) 2005 Jim Chandler <jim@n0vh.org>
5 * Portions Copyright (C) 2000-2019 The Xastir Group
6 *
7 * Portions copied from Bruce Bennett's excellent Davis WX work
8 *
9 * (see the files README and COPYING for more details)
10 *
11 * This file implements all of the database to APRS daemon.
12 *
13 *
14
15 LaCrosse/Data Base Weather --> APRS Weather
16
17 Intended use:
18
19 Create & provide APRS style packet string
20 without position information from MySQL database
21 weather information stored there by open2300
22 (See http://open2300.sourceforge.net/ for source) to
23 xastir-1.2.1 (See http://www.xastir.org for source)
24
25 Note: Open2300 is a weather data accumulator
26 aimed at LaCrosse weather stations, which stores weather
27 data in a mysql database.
28
29 Output is to the ip hostname:port required in the
30 command line.
31
32 ACKNOWLEGEMENTS:
33
34 Elements of this software are taken from wx200d ver 1.2
35 by Tim Witham <twitham@quiknet.com>, and it is modeled
36 after that application and from db2APRS by
37 Bruce Bennett <bruts@adelphia.net>.
38
39 *******************************************************************/
40 #include <config.h>
41 #include <defs.h>
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sys/socket.h>
47 #include <sys/stat.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 #include <netdb.h>
51 #include <signal.h>
52 #include <syslog.h>
53 #include <errno.h>
54 #include <getopt.h>
55 #include <mysql.h>
56
57 #define MAXARGS 20 /* maximum CGI args to parse */
58 #define TMPLEN 128 /* max length of CGI */
59 #define BUFLEN 32 /* max length of hostname:port */
60
61 #define VALID_WINDDIR 0x01
62 #define VALID_WINDSPD 0x02
63 #define VALID_WINDGST 0x04
64 #define VALID_TEMP 0x08
65 #define VALID_RAIN1HR 0x10
66 #define VALID_RAIN24H 0x20
67 #define VALID_HUMIDITY 0x40
68 #define VALID_AIRPRESS 0x80
69 #define VALID_RAINTOT 0x100
70
71 #define MTPS2MPH 2.2369
72 #define DEGC2DEGF 1.8
73 #define MM2IN100TH 3.937
74 #define INHG2HPA10TH 338.638
75
76 #define OUTDOOR_SENSOR 1
77
78 //---From the static table "mfield", which really should be dynamically read here---
79 // (but then I couldn't use a switch statement)
80
81 #define TEMPERATURE 0
82 #define TEMPERATURE_MIN 1
83 #define TEMPERATURE_MAX 2
84 #define HUMIDITY 10
85 #define HUMIDITY_MIN 11
86 #define HUMIDITY_MAX 12
87 #define WETNESS 20
88 #define WETNESS_MIN 21
89 #define WETNESS_MAX 22
90 #define AIR_PRESSURE 30
91 #define AIR_PRESSURE_MIN 31
92 #define AIR_PRESSURE_MAX 32
93 #define SOLAR 40
94 #define UV 41
95 #define RAIN 50
96 // note: "51" is really rain total
97 #define RAIN_PER_DAY 51
98 #define RAIN_PER_HOUR 52
99 #define WIND_SPEED 60
100 #define WIND_DIRECTION 61
101 #define WIND_GUST 62
102 #define WIND_X 63
103 #define WIND_Y 64
104 #define MOISTURE 70
105 #define WATERLEVEL 71
106 #define WATERLEVEL_MIN 72
107 #define WATERLEVEL_MAX 73
108 #define BATTERY 110
109 #define TRANSMITTER 111
110 #define DURATION 120
111 #define SAMPLES 121
112
113 struct dbinfo
114 {
115 char user[30];
116 char pswrd[15];
117 char name[30];
118 } db;
119
120 char *progname;
121 char *query;
122 int *current = 0;
123
124 int opt;
125
126 MYSQL mysql; // Yeah, globals...
127 MYSQL_RES *result;
128 MYSQL_ROW row;
129
130 char last_timestamp[20];
131
132 int debug_level;
133
134 char wxAPRShost[BUFLEN];
135 int wxAPRSport = PORT;
136 int outdoor_instr = OUTDOOR_SENSOR;
137
138
139
140
141
142 /******************************************************************
143 1/4/2003
144 Usage brief
145
146 *******************************************************************/
usage(int ret)147 void usage(int ret)
148 {
149 if (query)
150 {
151 printf("Content-type: text/plain\nStatus: 200\n\n");
152 }
153 printf("usage: %s [options] \n",progname);
154 printf("VERSION: %s\n",VERSION);
155 printf(" -h --help show this help and exit\n");
156 printf(" -v --verbose debugging info --> stderr\n");
157 printf(" -c --cport [port#] IP port for data output\n");
158 printf(" -u --user [database user] username for mysql - default meteo\n");
159 printf(" -p --password [db passwd] password for mysql - default none\n");
160 printf(" -b --database [database] database name - default meteo\n");
161 printf(" -n --nodaemon do not run as daemon\n");
162 printf(" -r --repeat keep running\n");
163 printf("options may be uniquely abbreviated; units are as defined in APRS\n");
164 printf("Specification 1.0.1 for positionless weather data (English/hPa).\n");
165 exit(ret);
166 }
167
168
169
170
171
172 /******************************************************************
173 1/2/2003
174 Make an APRS string out of WX data
175
176 *******************************************************************/
177
APRS_str(char * APRS_buf,double winddir,double windspeed,double windgust,double temp,double rain1hr,double rain24h,double raintot,double humidity,double airpressure,unsigned int valid_data_flgs,int Metric_Data)178 int APRS_str(char *APRS_buf,
179 double winddir,
180 double windspeed,
181 double windgust,
182 double temp,
183 double rain1hr,
184 double rain24h,
185 double raintot,
186 double humidity,
187 double airpressure,
188 unsigned int valid_data_flgs,
189 int Metric_Data)
190 {
191
192 int intval;
193 char pbuf[10];
194
195 if (APRS_buf == NULL)
196 {
197 if (debug_level & 1)
198 {
199 fprintf(stderr,"err: Null string buffer for APRS string.\n");
200 }
201 return 0; // Ooo!! *****Nasty Bad Exit Point Here****
202
203 }
204 if (valid_data_flgs & VALID_WINDDIR)
205 {
206 intval = (winddir + 0.5); // rounding to whole degrees
207 if (intval > 360)
208 {
209 if (debug_level & 1)
210 {
211 fprintf(stderr,"err: Wind direction > 360\n");
212 }
213 sprintf(APRS_buf, "c...");
214 }
215 else if (intval < 0)
216 {
217 if (debug_level & 1)
218 {
219 fprintf(stderr,"err: Wind direction negative\n");
220 }
221 sprintf(APRS_buf, "c...");
222
223 }
224 else
225 {
226 sprintf(APRS_buf, "c%0.3d", intval);
227 }
228 }
229 else
230 {
231 if (debug_level & 1)
232 {
233 fprintf(stderr,"info: Wind direction flagged as invalid\n");
234 }
235 sprintf(APRS_buf, "c...");
236
237 }
238
239 if (valid_data_flgs & VALID_WINDSPD)
240 {
241 if (Metric_Data)
242 {
243 intval = (windspeed*MTPS2MPH + 0.5); // converting & rounding to whole MPH
244 }
245 else
246 {
247 intval = (windspeed + 0.5); // rounding to whole MPH
248 }
249 if (intval > 600) // Let's be reasonable here - center of a tornado??
250 {
251 if (debug_level & 1)
252 {
253 fprintf(stderr,"err: Wind speed > 600 MPH\n");
254 }
255 sprintf(pbuf, "s...");
256
257 }
258 else if (intval < 0)
259 {
260 if (debug_level & 1)
261 {
262 fprintf(stderr,"err: Wind speed negative\n");
263 }
264 sprintf(pbuf, "s...");
265
266 }
267 else
268 {
269 sprintf(pbuf, "s%0.3d", intval);
270 }
271 }
272 else
273 {
274 if (debug_level & 1)
275 {
276 fprintf(stderr,"info: Wind speed flagged as invalid\n");
277 }
278 sprintf(pbuf, "s...");
279
280 }
281 strcat(APRS_buf,pbuf);
282
283 if (valid_data_flgs & VALID_WINDGST)
284 {
285 if (Metric_Data)
286 {
287 intval = (windgust*MTPS2MPH + 0.5); // converting & rounding to whole MPH
288 }
289 else
290 {
291 intval = (windgust + 0.5); // rounding to whole MPH
292 }
293 if (intval > 600) // Let's be reasonable here - center of a tornado??
294 {
295 if (debug_level & 1)
296 {
297 fprintf(stderr,"err: Wind gust > 600 MPH\n");
298 }
299 sprintf(pbuf, "g...");
300
301 }
302 else if (intval < 0)
303 {
304 if (debug_level & 1)
305 {
306 fprintf(stderr,"err: Wind speed negative\n");
307 }
308 sprintf(pbuf, "g...");
309
310 }
311 else
312 {
313 sprintf(pbuf, "g%0.3d", intval);
314 }
315
316 }
317 else
318 {
319 if (debug_level & 1)
320 {
321 fprintf(stderr,"info: Wind gust flagged as invalid\n");
322 }
323 sprintf(pbuf, "g...");
324
325 }
326 strcat(APRS_buf,pbuf);
327
328 if (valid_data_flgs & VALID_TEMP)
329 {
330 if (Metric_Data)
331 {
332 intval = ((temp)*DEGC2DEGF + 0.5)+32; // converting & rounding to whole Deg F
333 }
334 else
335 {
336 intval = (temp + 0.5); // rounding to whole Deg F
337 }
338 if (intval > 200) // Let's be reasonable here - boiling?
339 {
340 if (debug_level & 1)
341 {
342 fprintf(stderr,"err: Temperature > 200 Deg F\n");
343 }
344 sprintf(pbuf, "t...");
345
346 }
347 else if (intval < -99)
348 {
349 if (debug_level & 1)
350 {
351 fprintf(stderr,"err: Temperature < -99 Deg F\n");
352 }
353 sprintf(pbuf, "t...");
354
355 }
356 else
357 {
358 if (intval < 0)
359 {
360 sprintf(pbuf,"t%0.2d",intval);
361 }
362 else
363 {
364 sprintf(pbuf, "t%0.3d", intval);
365 }
366 }
367
368 }
369 else
370 {
371 if (debug_level & 1)
372 {
373 fprintf(stderr,"info: Temperature flagged as invalid\n");
374 }
375 sprintf(pbuf, "t...");
376
377 }
378 strcat(APRS_buf,pbuf);
379
380 if (valid_data_flgs & VALID_RAIN1HR)
381 {
382 if (Metric_Data)
383 {
384 intval = ((rain1hr)*MM2IN100TH + 0.5); // converting & rounding to whole 1/100 inch
385 }
386 else
387 {
388 intval = (rain1hr*100.0 + 0.5); // rounding to whole 1/100 inch
389 }
390 if (intval > 999) // 10 in/hr? Garden Hose -> rain gauge?
391 {
392 if (debug_level & 1)
393 {
394 fprintf(stderr,"err: Rainfall/Hr > 9.99 inch\n");
395 }
396 sprintf(pbuf, "\0\0\0\0");
397
398 }
399 else if (intval < -99)
400 {
401 if (debug_level & 1)
402 {
403 fprintf(stderr,"err: Rainfall/Hr negative\n");
404 }
405 sprintf(pbuf, "\0\0\0\0");
406
407 }
408 else
409 {
410 sprintf(pbuf, "r%0.3d", intval);
411 }
412 }
413 else
414 {
415
416 if (debug_level & 1)
417 {
418 fprintf(stderr,"info: Rainfall/Hr flagged as invalid\n");
419 }
420 sprintf(pbuf, "\0\0\0\0");
421
422 }
423 strcat(APRS_buf,pbuf);
424
425 if (valid_data_flgs & VALID_RAIN24H)
426 {
427 if (Metric_Data)
428 {
429 intval = ((rain24h)*MM2IN100TH + 0.5); // converting & rounding to whole 1/100 inch
430 }
431 else
432 {
433 intval = (rain24h*100.0 + 0.5); // rounding to whole 1/100 inch
434 }
435 if (intval > 999) // Can't handle greater than 9.99 inches of rain in 24 hours
436 {
437 if (debug_level & 1)
438 {
439 fprintf(stderr,"err: Rainfall/24Hr > 9.99 inch - reporting 9.99 inches\n");
440 }
441 sprintf(pbuf, "p999");
442
443 }
444 else if (intval < -99)
445 {
446 if (debug_level & 1)
447 {
448 fprintf(stderr,"err: Rainfall/Hr negative\n");
449 }
450 sprintf(pbuf, "\0\0\0\0");
451
452 }
453 else
454 {
455 sprintf(pbuf, "p%0.3d", intval);
456 }
457 }
458 else
459 {
460 if (debug_level & 1)
461 {
462 fprintf(stderr,"info: Rainfall/24Hr flagged as invalid\n");
463 }
464 sprintf(pbuf, "\0\0\0\0");
465
466 }
467 strcat(APRS_buf,pbuf);
468
469
470 if (valid_data_flgs & VALID_HUMIDITY)
471 {
472 intval = (humidity + 0.5); // rounding to whole percent
473 if (intval > 100) // Unlike the space shuttle engines, 100 % is max
474 {
475 if (debug_level & 1)
476 {
477 fprintf(stderr,"err: Humidity reported > 100%\n");
478 }
479 sprintf(pbuf, "\0\0\0\0");
480
481 }
482 else if (intval < 1)
483 {
484 if (debug_level & 1)
485 {
486 fprintf(stderr,"err: Humidity reported < 1%\n");
487 }
488 sprintf(pbuf, "\0\0\0\0");
489
490 }
491 else
492 {
493 if (intval == 100) // Report 100% as 'h00'
494 {
495 intval = 0;
496 }
497 sprintf(pbuf, "h%0.2d", intval);
498 }
499 }
500 else
501 {
502 if (debug_level & 1)
503 {
504 fprintf(stderr,"info: Humidity flagged as invalid\n");
505 }
506 sprintf(pbuf, "\0\0\0\0");
507
508 }
509 strcat(APRS_buf,pbuf);
510
511 if (valid_data_flgs & VALID_AIRPRESS)
512 {
513 if (Metric_Data)
514 {
515 intval = (airpressure*10.0 + 0.5); // rounding to whole tenth of a hPa
516 }
517 else
518 {
519 intval = (airpressure*INHG2HPA10TH + 0.5); // convering In-Hg to 1/10 hPa and rounding
520 }
521 if (intval > 20000) //two atmospheres - about 29 PSIA
522 {
523 if (debug_level & 1)
524 {
525 fprintf(stderr,"err: Air Pressure reported > 2 Atmospheres%\n");
526 }
527 sprintf(pbuf, "\0\0\0\0");
528
529 }
530 else if (intval < 0)
531 {
532 if (debug_level & 1)
533 {
534 fprintf(stderr,"err: Air Pressure reported negative%\n");
535 }
536 sprintf(pbuf, "\0\0\0\0");
537
538 }
539 else
540 {
541 sprintf(pbuf, "b%0.5d", intval);
542 }
543 }
544 else
545 {
546 if (debug_level & 1)
547 {
548 fprintf(stderr,"info: Air Pressure flagged as invalid\n");
549 }
550 sprintf(pbuf, "\0\0\0\0");
551
552 }
553 strcat(APRS_buf,pbuf);
554
555 // NOW THIS MAKES THE STRING NO LONGER A VALID APRS WX REPORT, but
556 // we don't care: Xastir does NOT just transmit this string, it parses it
557 // and re-recreates the correct string to transmit. We do this because
558 // APRS doesn't have a "total rain" string in its weather report, but
559 // Xastir likes to have that value around.
560 if (valid_data_flgs & VALID_RAINTOT)
561 {
562 if (Metric_Data)
563 {
564 intval = ((raintot)*MM2IN100TH + 0.5); // converting & rounding to whole 1/100 inch
565 }
566 else
567 {
568 intval = (raintot*100.0 + 0.5); // rounding to whole 1/100 inch
569 }
570
571 // Can't handle greater than 99.99 inches of total rain
572 if (intval > 9999)
573 {
574 if (debug_level & 1)
575 {
576 fprintf(stderr,"err: total Rainfall > 99.99 inch - reporting 9.99 inches\n");
577 }
578 sprintf(pbuf, "T9999");
579
580 }
581 else if (intval < -99)
582 {
583 if (debug_level & 1)
584 {
585 fprintf(stderr,"err: total Rainfall negative\n");
586 }
587 sprintf(pbuf, "\0\0\0\0\0");
588
589 }
590 else
591 {
592 sprintf(pbuf, "T%0.4d", intval);
593 }
594 }
595 else
596 {
597 if (debug_level & 1)
598 {
599 fprintf(stderr,"info: total Rainfall flagged as invalid\n");
600 }
601 sprintf(pbuf, "\0\0\0\0\0");
602
603 }
604 strcat(APRS_buf,pbuf);
605
606 strcat(APRS_buf,"xDvs\n"); // add X aprs and LaCrosse WX station ID's and <lf>
607
608 if (debug_level & 1)
609 {
610 fprintf(stderr,"\ninfo: APRS Version of WX - %s\n\n",APRS_buf);
611 }
612 return strlen(APRS_buf);
613 }
614
615
616
617
618
619 /******************************************************************
620 1/2/2003
621 Get the latest set of Weather Data from the Data Base
622
623 *******************************************************************/
624
Get_Latest_WX(double * winddir,double * windspeed,double * windgust,double * temp,double * rain1hr,double * rain24h,double * raintot,double * humidity,double * airpressure,unsigned int * valid_data_flgs,int * Metric_Data)625 int Get_Latest_WX( double *winddir,
626 double *windspeed,
627 double *windgust,
628 double *temp,
629 double *rain1hr,
630 double *rain24h,
631 double *raintot,
632 double *humidity,
633 double *airpressure,
634 unsigned int *valid_data_flgs,
635 int *Metric_Data)
636
637 {
638
639 char query_buffer[160];
640 int nrows, item_count;
641 int nfields;
642
643
644 // Find latest, see if it's new to us
645 // --new to us is a simple timestamp follower, so upon startup
646 // it will always read one set of data, assuming any exists
647
648 if (mysql_query(&mysql, "SELECT MAX(timestamp) from weather"))
649 {
650 if (debug_level & 1)
651 {
652 fprintf(stderr,"err: Latest timestamp query failed - exiting: %s\n", mysql_error(&mysql));
653 }
654 return 0;
655 }
656
657 if (!(result = mysql_store_result(&mysql)))
658 {
659 if (debug_level & 1)
660 {
661 fprintf(stderr,"err: Latest timestamp query failed - exiting: %s\n", mysql_error(&mysql));
662 }
663 return 0;
664 }
665
666 if (mysql_num_rows(result) != 1 )
667 {
668 if (debug_level & 1)
669 {
670 fprintf(stderr,"err: Latest timestamp query failed - exiting: number of results %d\n",
671 mysql_num_rows(result));
672 }
673 // release query buffer
674 mysql_free_result(result);
675 return 0;
676 }
677
678 row = mysql_fetch_row(result);
679
680 if ( row[0] == NULL )
681 {
682 if (debug_level & 1)
683 {
684 fprintf(stderr,"err: NULL result for timestamp query\n");
685 }
686 // release query buffer
687 mysql_free_result(result);
688 return 0;
689 }
690 // if no new data. exit with negative status
691
692 if (!strncmp(last_timestamp, row[0], 14))
693 {
694 if (debug_level & 1)
695 {
696 fprintf(stderr,"info: No new weather data recorded - exiting: negative data\n");
697 }
698 // release query buffer
699 mysql_free_result(result);
700 return -1;
701 }
702 strcpy(last_timestamp, row[0]); // For next pass & following query
703
704 if ( debug_level & 1)
705 {
706 fprintf(stdout,"Timestamp: %s\n",last_timestamp);
707 }
708
709 // release query buffer
710 mysql_free_result(result);
711
712 sprintf(query_buffer,"SELECT wind_angle, windspeed, temp_out, rain_1h, rain_24h, rel_hum_out, rel_pressure, rain_total FROM weather WHERE timestamp = %s", last_timestamp);
713
714 if (mysql_query(&mysql, query_buffer))
715 {
716 if (debug_level & 1)
717 {
718 fprintf(stderr,"err: Latest Weather Data query failed - exiting: %s\n\t --Query: %s\n",
719 mysql_error(&mysql), query_buffer);
720 }
721 return 0;
722 }
723
724 if (!(result = mysql_store_result(&mysql)))
725 {
726 if (debug_level & 1)
727 {
728 fprintf(stderr,"err: Latest Weather Data query failed - exiting: %s\n", mysql_error(&mysql));
729 }
730 return 0;
731 }
732 if ((nrows=mysql_num_rows(result)) < 1 )
733 {
734 if (debug_level & 1)
735 {
736 fprintf(stderr,"err: Latest Weather Data query failed - exiting: number of results %d\n",nrows);
737 }
738 // release query buffer
739 mysql_free_result(result);
740 return 0;
741 }
742 else
743 {
744 nfields=mysql_num_fields(result);
745 row=mysql_fetch_row(result);
746 if (debug_level & 1)
747 {
748 fprintf(stderr,"info: Latest Weather Data query: number of types of readings %d\n",nfields);
749 }
750 }
751
752 *valid_data_flgs = 0;
753 item_count = 0;
754
755 //WIND_DIRECTION :
756 *winddir = strtod(row[0],NULL);
757 *valid_data_flgs |= VALID_WINDDIR;
758 item_count++;
759 if (debug_level & 1)
760 {
761 fprintf(stderr,"wind direction %f\n ",*winddir);
762 }
763
764 //case WIND_SPEED :
765 *windspeed = strtod(row[1],NULL);
766 *windspeed = *windspeed * 1.15077945; // Convert from knots to mph
767 *valid_data_flgs |= VALID_WINDSPD;
768 item_count++;
769 if (debug_level & 1)
770 {
771 fprintf(stderr,"wind speed %f\n ",*windspeed);
772 }
773
774 //case WIND_GUST :
775 *windgust = strtod("0",NULL);
776 //*valid_data_flgs |= VALID_WINDGST; No gust information from open2300
777 item_count++;
778 if (debug_level & 1)
779 {
780 fprintf(stderr,"wind gust speed %f\n ",*windgust);
781 }
782
783 //case TEMPERATURE :
784 *temp = strtod(row[2],NULL);
785 *valid_data_flgs |= VALID_TEMP;
786 item_count++;
787 if (debug_level & 1)
788 {
789 fprintf(stderr,"temperature %f\n ",*temp);
790 }
791
792 //case RAIN_PER_HOUR :
793 *rain1hr = strtod(row[3],NULL);
794 *valid_data_flgs |= VALID_RAIN1HR;
795 item_count++;
796 if (debug_level & 1)
797 {
798 fprintf(stderr,"rainfall for 1 hr %f\n ",*rain1hr);
799 }
800
801 //case RAIN_PER_DAY :
802 *rain24h = strtod(row[4],NULL);
803 *valid_data_flgs |= VALID_RAIN24H;
804 item_count++;
805 if (debug_level & 1)
806 {
807 fprintf(stderr,"rainfall for 24 hrs %f\n ",*rain24h);
808 }
809
810 //case HUMIDITY :
811 *humidity = strtod(row[5],NULL);
812 *valid_data_flgs |= VALID_HUMIDITY;
813 item_count++;
814 if (debug_level & 1)
815 {
816 fprintf(stderr,"humidity %f\n ",*humidity);
817 }
818
819 //case AIR_PRESSURE :
820 *airpressure = strtod(row[6],NULL);
821 *valid_data_flgs |= VALID_AIRPRESS;
822 item_count++;
823 if (debug_level & 1)
824 {
825 fprintf(stderr,"air pressure %f\n ",*airpressure);
826 }
827
828 //case RAIN_TOTAL
829 *raintot = strtod(row[7],NULL);
830 *valid_data_flgs |= VALID_RAINTOT;
831 item_count++;
832 if (debug_level & 1)
833 {
834 fprintf(stderr,"rainfall since reset %f\n ",*raintot);
835 }
836
837 *Metric_Data = 0; // My station reports F, knots and inHG
838 // release query buffer & close connection
839 mysql_free_result(result);
840
841 if (debug_level & 1)
842 {
843 fprintf(stderr,"info: success - Weather Data number of reading types %d\n",
844 item_count);
845 }
846
847 return item_count;
848 }
849
850
851
852
853
854 /******************************************************************
855 1/5/2003
856 SIGPIPE signal handler
857
858 *******************************************************************/
pipe_handler(int sig)859 void pipe_handler(int sig) /* */
860 {
861 signal(SIGPIPE, SIG_IGN);
862 if (sig == SIGPIPE) // client went bye-bye
863 {
864 shutdown(*current, 2);
865 close(*current);
866 *current = -1;
867 if (debug_level & 1)
868 {
869 fprintf(stderr, "info: %s - TCP client timed out", progname);
870 }
871 }
872 }
873
874
875
876
877
878 /******************************************************************
879 1/5/2003
880 SIGTERM signal handler
881
882 *******************************************************************/
term_handler(int UNUSED (sig))883 void term_handler( int UNUSED(sig) ) /* */
884 {
885 if (debug_level & 1)
886 {
887 fprintf(stderr, "info: %s - ordered to DIE, complying", progname);
888 }
889
890 // release query buffer & close connection
891 mysql_free_result(result);
892
893 mysql_close(&mysql);
894
895 exit( 0 );
896 }
897
898
899
900
901
902 /******************************************************************
903 1/2/2003
904 Coordinating MAIN point
905
906 *******************************************************************/
main(int argc,char ** argv)907 int main(int argc, char **argv)
908 {
909 const char *flags = "Hhvnc:u:p:d:s:r";
910 char WX_APRS[120];
911 int data_len = 0 ;
912 double winddir;
913 double windspeed;
914 double windgust;
915 double temp;
916 double rain1hr;
917 double rain24h;
918 double raintot;
919 double humidity;
920 double airpressure;
921 unsigned int valid_data_flgs;
922 int Metric_Data = 0, dsts = 0;
923 int pid, s;
924 socklen_t clen = sizeof(struct sockaddr_in);
925 int fd[CONNECTIONS];
926 int *max = 0, dly_cnt = 1;
927 int not_a_daemon = 0, repetitive = 0, tcp_wx_port = PORT;
928 int i, index = 0;
929 struct sockaddr_in server, client;
930 struct in_addr bind_address;
931 fd_set rfds;
932 struct timeval tv;
933
934 struct option longopt[] =
935 {
936 {"help", 0, 0, 'h'},
937 {"refresh", 0, 0, 'r'},
938 {"verbose", 0, 0, 'v'},
939 {"user", 1, 0, 'u'},
940 {"password", 1, 0, 'p'},
941 {"database", 1, 0, 'd'},
942 {"cport", 1, 0, 'c'},
943 {"nodaemon", 0, 0, 'n'},
944 {0, 0, 0, 0}
945 };
946
947 debug_level = 0;
948
949 strcpy(db.user,"open2300"); // set default values for database access
950 strcpy(db.name,"open2300");
951 memset(db.pswrd,0,15);
952
953 mysql_init(&mysql);
954
955
956 progname = strrchr(argv[0], '/');
957 if (progname == NULL)
958 {
959 progname = argv[0];
960 }
961 else
962 {
963 progname++;
964 }
965
966 while ((opt = getopt_long(argc, argv, flags, longopt, &index)) != EOF)
967 {
968 switch (opt) /* parse command-line or CGI options */
969 {
970 case 'r':
971 repetitive = 1;
972 break;
973 case 'v':
974 fprintf(stdout,"Verbose mode set:\n");
975 debug_level = 1;
976 break;
977 case 'u': // mysql username
978 strncpy(db.user,(char *)optarg,30);
979 break;
980 case 'p': // mysql password
981 strncpy(db.pswrd,(char *)optarg,15);
982 break;
983 case 'd': // mysql database name
984 strncpy(db.name,(char *)optarg,30);
985 break;
986 case 'n': /* do not fork and become a daemon */
987 not_a_daemon = 1;
988 break;
989 case 'c': /* port to use */
990 tcp_wx_port = strtol(optarg, NULL, 0);
991 break;
992
993 case '?':
994 case 'h':
995 case 'H':
996 usage(0);
997 break;
998 default :
999 usage(1);
1000 }
1001 }
1002 if (debug_level & 1)
1003 {
1004 fprintf(stdout,"Starting...");
1005 if (repetitive)
1006 {
1007 fprintf(stdout, " forever ");
1008 }
1009 else
1010 {
1011 fprintf(stdout, " one pass only ");
1012 }
1013 fprintf(stdout," with database user=%s, password=%s, for database=%s\n",
1014 db.user, db.pswrd, db.name);
1015 if (not_a_daemon)
1016 {
1017 fprintf(stdout," as a program ");
1018 }
1019 else
1020 {
1021 fprintf(stdout," as a daemon ");
1022 }
1023 fprintf(stdout, " using TCP port %d\n",tcp_wx_port);
1024 }
1025
1026 // Data base connection
1027
1028
1029 if (!(mysql_real_connect(&mysql, "localhost", db.user, db.pswrd, db.name, 0, NULL, 0)))
1030 {
1031 if (debug_level & 1)
1032 {
1033 fprintf(stderr,"err: Data Base connect for user:%s to database:%s failed - exiting: \n\t%s\n",
1034 db.user, db.name, mysql_error(&mysql));
1035 }
1036 exit(9);
1037 }
1038
1039 server.sin_family = AF_INET;
1040 bind_address.s_addr = htonl(INADDR_ANY);
1041 server.sin_addr = bind_address;
1042 server.sin_port = htons(tcp_wx_port);
1043
1044 if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
1045 {
1046 if (debug_level & 1)
1047 {
1048 fprintf(stderr, "err: %s - no socket", progname);
1049 }
1050 exit(10);
1051 }
1052 /* <dirkx@covalent.net> / April 2001 Minor change to allow quick
1053 * (re)start of deamon or client while there are pending
1054 * conncections during the quit. To avoid addresss/port in use
1055 * error. */
1056 i = 1;
1057 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) == -1)
1058 {
1059 if (debug_level & 1)
1060 {
1061 fprintf(stderr, "err: %s - setsockopt", progname);
1062 }
1063 }
1064 if (bind(s, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1)
1065 {
1066 if (debug_level & 1)
1067 {
1068 fprintf(stderr, "err: %s - cannot bind to socket", progname);
1069 }
1070 exit(11);
1071 }
1072 if (listen(s, CONNECTIONS) == -1)
1073 {
1074 if (debug_level & 1)
1075 {
1076 fprintf(stderr, "err: %s - listen", progname);
1077 }
1078 exit(12);
1079 }
1080
1081 if (debug_level & 1)
1082 {
1083 fprintf(stdout,"Sockets UP.\n");
1084 }
1085
1086 umask(0022);
1087 for (i = 0; i < CONNECTIONS; i++)
1088 {
1089 fd[i] = -1;
1090 }
1091 tv.tv_sec = 0;
1092 tv.tv_usec = 0;
1093
1094 if (!not_a_daemon) /* setup has worked; now become a daemon? */
1095 {
1096
1097 if ((pid = fork()) == -1)
1098 {
1099 syslog(LOG_ERR, "can't fork() to become daemon: %m");
1100 exit(20);
1101 }
1102 else if (pid)
1103 {
1104 exit (0);
1105 }
1106
1107 setsid();
1108 for (i = 0; i < NOFILE; i++)
1109 {
1110 if ( i != s)
1111 {
1112 close(i);
1113 }
1114 }
1115 }
1116
1117 /* catch signals to close the database connection */
1118 signal( SIGTERM, term_handler );/* termination */
1119 #if defined(SIGPWR) /* SIGPWR is linux centric */
1120 signal( SIGPWR, term_handler ); /* power failure */
1121 #endif
1122
1123 if (debug_level & 1)
1124 {
1125 fprintf(stdout,"Main Loop...\n");
1126 }
1127 dly_cnt = 1; //N0VH, change back to 1
1128 do
1129 {
1130 if (!(dly_cnt--))
1131 {
1132 dly_cnt = 25; // Every 'dly_cnt' passes check for WX data update
1133 if ((dsts = Get_Latest_WX(&winddir,&windspeed,&windgust,
1134 &temp,&rain1hr,&rain24h,&raintot,
1135 &humidity,&airpressure,
1136 &valid_data_flgs,&Metric_Data)) !=0 )
1137 {
1138 if ( dsts > 0 )
1139 {
1140 data_len = APRS_str(WX_APRS, winddir,windspeed,windgust,
1141 temp, rain1hr, rain24h, raintot,
1142 humidity, airpressure,
1143 valid_data_flgs, Metric_Data);
1144
1145 if (!data_len)
1146 {
1147 if (debug_level & 1)
1148 {
1149 fprintf(stderr, "err: WX info formatting problem!");
1150 }
1151 exit(13);
1152 }
1153 }
1154 }
1155 else
1156 {
1157 exit(dsts);
1158 }
1159 }
1160 FD_ZERO(&rfds);
1161 FD_SET(s, &rfds);
1162 if (select(s + 1, &rfds, NULL, NULL, &tv) > 0)
1163 {
1164 for (current = fd; (*current > 0) && (current < fd + CONNECTIONS - 1); current++);
1165 if (current > max)
1166 {
1167 max = current;
1168 }
1169 if ((*current = accept(s, (struct sockaddr *)&client, &clen)) != -1)
1170 {
1171 write(*current, WX_APRS, data_len);
1172 }
1173 }
1174 if (dly_cnt == 25)
1175 {
1176 if (debug_level & 1)
1177 {
1178 fprintf(stdout,"Updating clients:");
1179 }
1180 for (current = fd; current <=max; current++)
1181 {
1182 if (*current > 0) // active socket
1183 {
1184 if (debug_level & 1)
1185 {
1186 fprintf(stdout," #");
1187 }
1188 signal(SIGPIPE, pipe_handler);
1189 write(*current, WX_APRS, data_len);
1190 }
1191 if (debug_level & 1)
1192 {
1193 fprintf(stdout," done\n");
1194 }
1195 }
1196 }
1197 sleep(1); //
1198 }
1199 while (repetitive);
1200
1201 mysql_close(&mysql);
1202
1203 if (debug_level & 1)
1204 {
1205 fprintf(stdout,"Exiting normally.\n");
1206 }
1207 exit(0);
1208 }
1209
1210
1211