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