enable silent rules, if available
[fso-gpsd.git] / fso-gpsd.c
blob53e3cb6559493d96088164f20cb9c69d31be67d5
1 /* fso-gpsd - a gpsd compatibility daemon for the FSO frameworkd
3 * Copyright (C) 2008 Sascha Wessel <wessel@nefkom.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <syslog.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <netdb.h>
28 #include <math.h>
29 #include <fcntl.h>
30 #include <sys/socket.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34 #include <dbus/dbus-glib.h>
35 #include <glib-object.h>
36 #include <glib.h>
38 #define KNOTS_TO_KPH (1.852)
39 #define KNOTS_TO_MPS (0.51444444)
41 /******************************************************************************/
43 #define ERROR(...) \
44 do { \
45 g_log(NULL, G_LOG_LEVEL_ERROR, __VA_ARGS__); \
46 abort(); \
47 } while (0)
49 #define CRITICAL(...) \
50 do { \
51 if (G_UNLIKELY(log_level & G_LOG_LEVEL_CRITICAL)) { \
52 g_log(NULL, G_LOG_LEVEL_CRITICAL, __VA_ARGS__); \
53 } \
54 } while (0)
56 #define WARNING(...) \
57 do { \
58 if (G_UNLIKELY(log_level & G_LOG_LEVEL_WARNING)) { \
59 g_log(NULL, G_LOG_LEVEL_WARNING, __VA_ARGS__); \
60 } \
61 } while (0)
63 #define MESSAGE(...) \
64 do { \
65 if (G_UNLIKELY(log_level & G_LOG_LEVEL_MESSAGE)) { \
66 g_log(NULL, G_LOG_LEVEL_MESSAGE, __VA_ARGS__); \
67 } \
68 } while (0)
70 #define INFO(...) \
71 do { \
72 if (G_UNLIKELY(log_level & G_LOG_LEVEL_INFO)) { \
73 g_log(NULL, G_LOG_LEVEL_INFO, __VA_ARGS__); \
74 } \
75 } while (0)
77 #define DEBUG(...) \
78 do { \
79 if (G_UNLIKELY(log_level & G_LOG_LEVEL_DEBUG)) { \
80 g_log(NULL, G_LOG_LEVEL_DEBUG, __VA_ARGS__); \
81 } \
82 } while (0)
84 /******************************************************************************/
86 #define POSITION_LATITUDE_IS_VALID (position_fields & (1 << 0))
87 #define POSITION_LONGITUDE_IS_VALID (position_fields & (1 << 1))
88 #define POSITION_ALTITUDE_IS_VALID (position_fields & (1 << 2))
90 #define ACCURACY_PDOP_IS_VALID (accuracy_fields & (1 << 0))
91 #define ACCURACY_HDOP_IS_VALID (accuracy_fields & (1 << 1))
92 #define ACCURACY_VDOP_IS_VALID (accuracy_fields & (1 << 2))
94 #define COURSE_SPEED_IS_VALID (course_fields & (1 << 0))
95 #define COURSE_DIRECTION_IS_VALID (course_fields & (1 << 1))
96 #define COURSE_CLIMB_IS_VALID (course_fields & (1 << 2))
98 /******************************************************************************/
100 struct client {
101 GIOChannel *channel;
102 gboolean watcher;
103 gboolean raw;
106 struct satellite {
107 unsigned prn;
108 gboolean used;
109 unsigned elevation;
110 unsigned azimuth;
111 unsigned snr;
114 /******************************************************************************/
116 static int log_level = G_LOG_LEVEL_ERROR
117 | G_LOG_LEVEL_CRITICAL
118 | G_LOG_LEVEL_WARNING;
120 static gboolean background = FALSE;
121 static gboolean nowait = FALSE;
123 /******************************************************************************/
125 static gboolean info_queued = FALSE;
126 static gboolean sats_queued = FALSE;
127 static gboolean nmea_queued = FALSE;
129 /******************************************************************************/
131 static gboolean timeout_handler_registered = FALSE;
133 /******************************************************************************/
135 static GList *client_list = NULL;
137 /******************************************************************************/
139 static DBusGConnection *connection = NULL;
141 /******************************************************************************/
143 static const char *gypsy_device = "";
144 static const char *gypsy_service = "org.freedesktop.Gypsy";
145 static const char *gypsy_server_path = "/org/freedesktop/Gypsy";
146 static char *gypsy_device_path = NULL;
148 static gboolean gypsy_device_create_failed = FALSE;
149 static gboolean gypsy_device_start_failed = FALSE;
151 /******************************************************************************/
153 #define GYPSY_SERVER "org.freedesktop.Gypsy.Server"
154 static DBusGProxy *server_proxy = NULL;
156 #define GYPSY_DEVICE "org.freedesktop.Gypsy.Device"
157 static DBusGProxy *device_proxy = NULL;
158 static gboolean device_connectionstatus = 0;
159 static int device_fixstatus = 0; /* 0=INVALID, 1=NONE, 2=2D, 3=3D */
161 #define GYPSY_TIME "org.freedesktop.Gypsy.Time"
162 static DBusGProxy *time_proxy = NULL;
163 static int time_seconds = 0;
165 #define GYPSY_POSITION "org.freedesktop.Gypsy.Position"
166 static DBusGProxy *position_proxy = NULL;
167 static int position_fields = 0;
168 static int position_timestamp = 0;
169 static double position_latitude = 0.0;
170 static double position_longitude = 0.0;
171 static double position_altitude = 0.0;
173 #define GYPSY_ACCURACY "org.freedesktop.Gypsy.Accuracy"
174 static DBusGProxy *accuracy_proxy = NULL;
175 static int accuracy_fields = 0;
176 static double accuracy_pdop = 0.0;
177 static double accuracy_hdop = 0.0;
178 static double accuracy_vdop = 0.0;
180 #define GYPSY_COURSE "org.freedesktop.Gypsy.Course"
181 static DBusGProxy *course_proxy = NULL;
182 static int course_fields = 0;
183 static int course_timestamp = 0;
184 static double course_speed = 0.0;
185 static double course_direction = 0.0;
186 static double course_climb = 0.0;
188 #define GYPSY_SATELLITE "org.freedesktop.Gypsy.Satellite"
189 static DBusGProxy *satellite_proxy = NULL;
190 #define SATELLITEC_MAX 12
191 static struct satellite satellite_satellitev[SATELLITEC_MAX];
192 static unsigned satellite_satellitec = 0;
194 /******************************************************************************/
196 static GString *nmea_gga;
197 static GString *nmea_gsa;
198 static GString *nmea_gsv[3];
199 static GString *nmea_rmc;
200 static GString *nmea_gll;
201 static GString *nmea_vtg;
203 /******************************************************************************/
205 static int
206 get_fixstatus(void)
208 if (POSITION_LATITUDE_IS_VALID && POSITION_LONGITUDE_IS_VALID) {
209 if (POSITION_ALTITUDE_IS_VALID) {
210 return MIN(device_fixstatus, 3);
211 } else {
212 return MIN(device_fixstatus, 2);
214 } else {
215 return MIN(device_fixstatus, 1);
219 static gboolean
220 check_period(const GTimeVal *tv1, const GTimeVal *tv2, glong sec)
222 glong tv1_sec = tv1->tv_sec + sec;
223 glong tv1_usec = tv1->tv_usec;
224 glong tv2_sec = tv2->tv_sec;
225 glong tv2_usec = tv2->tv_usec;
227 if (tv1_sec == tv2_sec) {
228 return (tv1_usec > tv2_usec);
229 } else {
230 return (tv1_sec > tv2_sec);
234 /******************************************************************************/
236 static void
237 shutdown_client(struct client *client)
239 if (client->channel) {
240 MESSAGE("Shutdown client");
242 g_io_channel_shutdown(client->channel, FALSE, NULL);
245 client->channel = NULL;
246 client->watcher = FALSE;
247 client->raw = FALSE;
250 static void
251 send_to_client(struct client *client, const char *string, gsize len)
253 GIOStatus status;
254 gsize bytes_written;
256 if (!client->channel) {
257 return;
260 status = g_io_channel_write_chars(client->channel, string,
261 len, &bytes_written, NULL);
263 if ((bytes_written != len) || (status != G_IO_STATUS_NORMAL)) {
264 shutdown_client(client);
268 /******************************************************************************/
270 static void
271 add_nmea_checksum(GString *nmea)
273 int checksum = 0;
274 char *p;
276 p = nmea->str;
278 if (*p == '$') {
279 p++;
282 while (*p) {
283 checksum ^= (int) *p;
284 p++;
287 g_string_append_printf(nmea, "*%02x", checksum);
290 static void
291 add_nmea_time(GString *nmea)
293 if (time_seconds <= 0) {
294 g_string_append_c(nmea, ',');
295 } else {
296 struct tm *tm;
297 time_t seconds;
299 seconds = time_seconds;
300 tm = gmtime(&seconds);
302 g_string_append_printf(nmea, ",%02d%02d%02d.00",
303 tm->tm_hour, tm->tm_min, tm->tm_sec);
307 static void
308 add_nmea_latitude(GString *nmea)
310 if (!POSITION_LATITUDE_IS_VALID) {
311 g_string_append_c(nmea, ',');
312 g_string_append_c(nmea, ',');
313 } else {
314 double a, d;
316 a = fabs(position_latitude);
317 d = floor(a);
319 g_string_append_printf(nmea, ",%010.5f,%c",
320 (d * 100.0) + ((a - d) * 60.0),
321 (position_latitude > 0) ? 'N' : 'S');
325 static void
326 add_nmea_longitude(GString *nmea)
328 if (!POSITION_LONGITUDE_IS_VALID) {
329 g_string_append_c(nmea, ',');
330 g_string_append_c(nmea, ',');
331 } else {
332 double a, d;
334 a = fabs(position_longitude);
335 d = floor(a);
337 g_string_append_printf(nmea, ",%011.5f,%c",
338 (d * 100.0) + ((a - d) * 60.0),
339 (position_longitude > 0) ? 'E' : 'W');
343 /******************************************************************************/
345 static void
346 update_nmea_gga(void)
348 unsigned i, used;
350 g_string_assign(nmea_gga, "$GPGGA");
352 add_nmea_time(nmea_gga);
354 add_nmea_latitude(nmea_gga);
356 add_nmea_longitude(nmea_gga);
358 g_string_append_printf(nmea_gga, ",%1d", MAX(get_fixstatus() - 1, 0));
360 for (i = 0, used = 0; i < satellite_satellitec; i++) {
361 if (satellite_satellitev[i].used) {
362 used++;
366 g_string_append_printf(nmea_gga, ",%02d", used);
368 if (!ACCURACY_HDOP_IS_VALID) {
369 g_string_append_c(nmea_gga, ',');
370 } else {
371 g_string_append_printf(nmea_gga, ",%04.2f", accuracy_hdop);
374 if (!POSITION_ALTITUDE_IS_VALID) {
375 g_string_append_c(nmea_gga, ',');
376 g_string_append_c(nmea_gga, ',');
377 } else {
378 g_string_append_printf(nmea_gga, ",%03.1f,M",
379 position_altitude);
382 g_string_append_c(nmea_gga, ','); /* FIXME */
383 g_string_append_c(nmea_gga, ',');
384 g_string_append_c(nmea_gga, ',');
385 g_string_append_c(nmea_gga, ',');
387 add_nmea_checksum(nmea_gga);
390 static void
391 update_nmea_gsa(void)
393 unsigned i;
395 g_string_assign(nmea_gsa, "$GPGSA");
397 g_string_append_c(nmea_gsa, ',');
398 g_string_append_c(nmea_gsa, 'A');
400 g_string_append_printf(nmea_gsa, ",%1d", MAX(get_fixstatus() - 1, 0));
402 for (i = 0; i < 12; i++) {
403 if (i < satellite_satellitec) {
404 g_string_append_printf(nmea_gsa, ",%02d",
405 satellite_satellitev[i].prn);
406 } else {
407 g_string_append_c(nmea_gsa, ',');
411 if (!ACCURACY_PDOP_IS_VALID) {
412 g_string_append_c(nmea_gsa, ',');
413 } else {
414 g_string_append_printf(nmea_gsa, ",%04.2f", accuracy_pdop);
417 if (!ACCURACY_HDOP_IS_VALID) {
418 g_string_append_c(nmea_gsa, ',');
419 } else {
420 g_string_append_printf(nmea_gsa, ",%04.2f", accuracy_hdop);
423 if (!ACCURACY_VDOP_IS_VALID) {
424 g_string_append_c(nmea_gsa, ',');
425 } else {
426 g_string_append_printf(nmea_gsa, ",%04.2f", accuracy_vdop);
429 add_nmea_checksum(nmea_gsa);
432 static void
433 update_nmea_gsv(void)
435 unsigned i;
436 unsigned sat_count;
437 unsigned sentence_count;
439 g_string_truncate(nmea_gsv[0], 0);
440 g_string_truncate(nmea_gsv[1], 0);
441 g_string_truncate(nmea_gsv[2], 0);
443 if (satellite_satellitec == 0) {
444 g_string_assign(nmea_gsv[0], "$GPGSV,1,1,00*79");
445 return;
448 sat_count = MIN(satellite_satellitec, 12);
449 sentence_count = ((sat_count - 1) / 4) + 1;
451 for (i = 0; i < sat_count; i++) {
452 int index_div, index_mod;
454 index_div = i / 4;
455 index_mod = i % 4;
457 if (index_mod == 0) {
458 g_string_append_printf(nmea_gsv[index_div],
459 "$GPGSV,%1d,%1d,%02d",
460 sentence_count,
461 index_div + 1,
462 sat_count);
465 g_string_append_printf(nmea_gsv[index_div],
466 ",%02u,%02u,%03u,%02u",
467 satellite_satellitev[i].prn,
468 satellite_satellitev[i].elevation,
469 satellite_satellitev[i].azimuth,
470 satellite_satellitev[i].snr);
473 for (i = 0; i < 3; i++) {
474 if (nmea_gsv[i]->len > 0) {
475 add_nmea_checksum(nmea_gsv[i]);
480 static void
481 update_nmea_rmc(void)
483 g_string_assign(nmea_rmc, "$GPRMC");
485 add_nmea_time(nmea_rmc);
487 if (get_fixstatus() > 1) {
488 g_string_append_c(nmea_rmc, ',');
489 g_string_append_c(nmea_rmc, 'A');
490 } else {
491 g_string_append_c(nmea_rmc, ',');
492 g_string_append_c(nmea_rmc, 'V');
495 add_nmea_latitude(nmea_rmc);
497 add_nmea_longitude(nmea_rmc);
499 if (!COURSE_SPEED_IS_VALID) {
500 g_string_append_c(nmea_rmc, ',');
501 } else {
502 g_string_append_printf(nmea_rmc, ",%07.3f",
503 course_speed);
506 if (!COURSE_DIRECTION_IS_VALID) {
507 g_string_append_c(nmea_rmc, ',');
508 } else {
509 g_string_append_printf(nmea_rmc, ",%06.2f",
510 course_direction);
513 if (time_seconds <= 0) {
514 g_string_append_c(nmea_rmc, ',');
515 } else {
516 struct tm *tm;
517 time_t seconds;
519 seconds = time_seconds;
520 tm = gmtime(&seconds);
522 g_string_append_printf(nmea_rmc, ",%02d%02d%02d",
523 tm->tm_mday, tm->tm_mon + 1, tm->tm_year % 100);
526 g_string_append_c(nmea_rmc, ',');
527 g_string_append_c(nmea_rmc, ',');
529 add_nmea_checksum(nmea_rmc);
532 static void
533 update_nmea_gll(void)
535 g_string_assign(nmea_gll, "$GPGLL");
537 add_nmea_latitude(nmea_gll);
539 add_nmea_longitude(nmea_gll);
541 add_nmea_time(nmea_gll);
543 if (get_fixstatus() > 1) {
544 g_string_append_c(nmea_gll, ',');
545 g_string_append_c(nmea_gll, 'A');
546 } else {
547 g_string_append_c(nmea_gll, ',');
548 g_string_append_c(nmea_gll, 'V');
551 add_nmea_checksum(nmea_gll);
554 static void
555 update_nmea_vtg(void)
557 g_string_assign(nmea_vtg, "$GPVTG");
559 if (!COURSE_DIRECTION_IS_VALID) {
560 g_string_append_c(nmea_vtg, ',');
561 g_string_append_c(nmea_vtg, ',');
562 } else {
563 g_string_append_printf(nmea_vtg, ",%04.2f,T", course_direction);
566 g_string_append_c(nmea_vtg, ','); /* FIXME */
567 g_string_append_c(nmea_vtg, ',');
569 if (!COURSE_SPEED_IS_VALID) {
570 g_string_append_c(nmea_vtg, ',');
571 g_string_append_c(nmea_vtg, ',');
572 g_string_append_c(nmea_vtg, ',');
573 g_string_append_c(nmea_vtg, ',');
574 } else {
575 g_string_append_printf(nmea_vtg, ",%05.3f,N,%05.3f,K",
576 course_speed, course_speed * KNOTS_TO_KPH);
579 add_nmea_checksum(nmea_vtg);
582 /******************************************************************************/
584 static gboolean
585 send_nmea_to_clients(gpointer data)
587 GString *string;
588 GList *l;
590 for (string = NULL, l = client_list; l; l = l->next) {
591 struct client *client = l->data;
593 if (client->raw) {
594 if (!string) {
595 static GTimeVal timestamp = { 0, 0 };
597 if (data && check_period(&timestamp, data, 1)) {
598 return FALSE;
601 g_get_current_time(&timestamp);
603 string = g_string_sized_new(1024);
605 if (!nmea_gga->len) {
606 update_nmea_gga();
608 if (!nmea_gsa->len) {
609 update_nmea_gsa();
611 if (!nmea_gsv[0]->len) {
612 update_nmea_gsv();
614 if (!nmea_rmc->len) {
615 update_nmea_rmc();
617 if (!nmea_gll->len) {
618 update_nmea_gll();
620 if (!nmea_vtg->len) {
621 update_nmea_vtg();
624 if (nmea_gga->len) {
625 INFO("Sending nmea: %s",
626 nmea_gga->str);
627 g_string_append_len(string,
628 nmea_gga->str,
629 nmea_gga->len);
630 g_string_append_c(string, '\r');
631 g_string_append_c(string, '\n');
633 if (nmea_gsa->len) {
634 INFO("Sending nmea: %s",
635 nmea_gsa->str);
636 g_string_append_len(string,
637 nmea_gsa->str,
638 nmea_gsa->len);
639 g_string_append_c(string, '\r');
640 g_string_append_c(string, '\n');
642 if (nmea_gsv[0]->len) {
643 INFO("Sending nmea: %s",
644 nmea_gsv[0]->str);
645 g_string_append_len(string,
646 nmea_gsv[0]->str,
647 nmea_gsv[0]->len);
648 g_string_append_c(string, '\r');
649 g_string_append_c(string, '\n');
651 if (nmea_gsv[1]->len) {
652 INFO("Sending nmea: %s",
653 nmea_gsv[1]->str);
654 g_string_append_len(string,
655 nmea_gsv[1]->str,
656 nmea_gsv[1]->len);
657 g_string_append_c(string, '\r');
658 g_string_append_c(string, '\n');
660 if (nmea_gsv[2]->len) {
661 INFO("Sending nmea: %s",
662 nmea_gsv[2]->str);
663 g_string_append_len(string,
664 nmea_gsv[2]->str,
665 nmea_gsv[2]->len);
666 g_string_append_c(string, '\r');
667 g_string_append_c(string, '\n');
669 if (nmea_rmc->len) {
670 INFO("Sending nmea: %s",
671 nmea_rmc->str);
672 g_string_append_len(string,
673 nmea_rmc->str,
674 nmea_rmc->len);
675 g_string_append_c(string, '\r');
676 g_string_append_c(string, '\n');
678 if (nmea_gll->len) {
679 INFO("Sending nmea: %s",
680 nmea_gll->str);
681 g_string_append_len(string,
682 nmea_gll->str,
683 nmea_gll->len);
684 g_string_append_c(string, '\r');
685 g_string_append_c(string, '\n');
687 if (nmea_vtg->len) {
688 INFO("Sending nmea: %s",
689 nmea_vtg->str);
690 g_string_append_len(string,
691 nmea_vtg->str,
692 nmea_vtg->len);
693 g_string_append_c(string, '\r');
694 g_string_append_c(string, '\n');
698 send_to_client(client, string->str, string->len);
702 if (string) {
703 g_string_free(string, TRUE);
706 nmea_queued = FALSE;
708 return FALSE;
711 static void
712 queue_send_nmea_to_clients(void)
714 if (nmea_queued) {
715 return;
718 nmea_queued = TRUE;
720 g_idle_add(send_nmea_to_clients, NULL);
723 /******************************************************************************/
725 static void
726 add_sats(GString *string)
728 unsigned i, count;
730 if (satellite_satellitec == 0) {
731 g_string_append(string, ",Y=?");
732 return;
735 g_string_append(string, ",Y=-");
737 if (time_seconds <= 0) {
738 g_string_append(string, " ? ");
739 } else {
740 g_string_append_printf(string, " %d.000 ", time_seconds);
743 count = MIN(satellite_satellitec, 12);
745 g_string_append_printf(string, "%d:", count);
747 for (i = 0; i < count; i++) {
748 g_string_append_printf(string, "%d %d %d %d %d:",
749 satellite_satellitev[i].prn,
750 satellite_satellitev[i].elevation,
751 satellite_satellitev[i].azimuth,
752 satellite_satellitev[i].snr,
753 satellite_satellitev[i].used);
757 /******************************************************************************/
759 static gboolean
760 send_sats_to_clients(gpointer data)
762 GString *string;
763 GList *l;
765 for (string = NULL, l = client_list; l; l = l->next) {
766 struct client *client = l->data;
768 if (client->watcher) {
769 if (!string) {
770 static GTimeVal timestamp = { 0, 0 };
772 if (data && check_period(&timestamp, data, 1)) {
773 return FALSE;
776 g_get_current_time(&timestamp);
778 string = g_string_sized_new(1024);
780 g_string_append(string, "GPSD");
782 add_sats(string);
784 INFO("Sending sats: %s", string->str);
786 g_string_append_c(string, '\r');
787 g_string_append_c(string, '\n');
790 send_to_client(client, string->str, string->len);
794 if (string) {
795 g_string_free(string, TRUE);
798 sats_queued = FALSE;
800 return FALSE;
803 static void
804 queue_send_sats_to_clients(void)
806 if (sats_queued) {
807 return;
810 sats_queued = TRUE;
812 g_idle_add(send_sats_to_clients, NULL);
815 /******************************************************************************/
817 static void
818 add_info(GString *string)
820 if (!device_connectionstatus || get_fixstatus() <= 1) {
821 g_string_append(string, ",O=?");
822 return;
825 g_string_append(string, ",O=-");
827 if (time_seconds <= 0) {
828 g_string_append_c(string, ' ');
829 g_string_append_c(string, '?');
830 } else {
831 g_string_append_printf(string, " %d.000", time_seconds);
834 g_string_append_c(string, ' '); /* FIXME */
835 g_string_append_c(string, '?');
837 if (!POSITION_LATITUDE_IS_VALID) {
838 g_string_append_c(string, ' ');
839 g_string_append_c(string, '?');
840 } else {
841 g_string_append_printf(string, " %.6f", position_latitude);
843 if (!POSITION_LONGITUDE_IS_VALID) {
844 g_string_append_c(string, ' ');
845 g_string_append_c(string, '?');
846 } else {
847 g_string_append_printf(string, " %.6f", position_longitude);
849 if (!POSITION_ALTITUDE_IS_VALID) {
850 g_string_append_c(string, ' ');
851 g_string_append_c(string, '?');
852 } else {
853 g_string_append_printf(string, " %.2f", position_altitude);
855 if (!ACCURACY_HDOP_IS_VALID) {
856 g_string_append_c(string, ' ');
857 g_string_append_c(string, '?');
858 } else {
859 g_string_append_printf(string, " %.2f", accuracy_hdop);
861 if (!ACCURACY_VDOP_IS_VALID) {
862 g_string_append_c(string, ' ');
863 g_string_append_c(string, '?');
864 } else {
865 g_string_append_printf(string, " %.2f", accuracy_vdop);
867 if (!COURSE_DIRECTION_IS_VALID) {
868 g_string_append_c(string, ' ');
869 g_string_append_c(string, '?');
870 } else {
871 g_string_append_printf(string, " %.4f", course_direction);
873 if (!COURSE_SPEED_IS_VALID) {
874 g_string_append_c(string, ' ');
875 g_string_append_c(string, '?');
876 } else {
877 g_string_append_printf(string, " %.3f",
878 course_speed * KNOTS_TO_MPS);
880 if (!COURSE_CLIMB_IS_VALID) {
881 g_string_append_c(string, ' ');
882 g_string_append_c(string, '?');
883 } else {
884 g_string_append_printf(string, " %.3f", course_climb);
887 g_string_append(string, " ? ? ?"); /* FIXME */
889 g_string_append_printf(string, " %d", get_fixstatus());
892 /******************************************************************************/
894 static gboolean
895 send_info_to_clients(gpointer data)
897 GString *string;
898 GList *l;
900 for (string = NULL, l = client_list; l; l = l->next) {
901 struct client *client = l->data;
903 if (client->watcher) {
904 if (!string) {
905 static GTimeVal timestamp = { 0, 0 };
907 if (data && check_period(&timestamp, data, 1)) {
908 return FALSE;
911 g_get_current_time(&timestamp);
913 string = g_string_sized_new(1024);
915 g_string_append(string, "GPSD");
917 add_info(string);
919 INFO("Sending info: %s", string->str);
921 g_string_append_c(string, '\r');
922 g_string_append_c(string, '\n');
925 send_to_client(client, string->str, string->len);
929 if (string) {
930 g_string_free(string, TRUE);
933 info_queued = FALSE;
935 return FALSE;
938 static void
939 queue_send_info_to_clients(void)
941 if (info_queued) {
942 return;
945 info_queued = TRUE;
947 g_idle_add(send_info_to_clients, NULL);
950 /******************************************************************************/
952 static void
953 connection_status_changed(
954 DBusGProxy *proxy,
955 gboolean connectionstatus,
956 gpointer user_data
959 device_connectionstatus = connectionstatus;
961 INFO(GYPSY_DEVICE": ConnectionStatusChanged(connectionstatus=%s)",
962 device_connectionstatus ? "TRUE" : "FALSE");
965 static void
966 fix_status_changed(
967 DBusGProxy *proxy,
968 int fixstatus,
969 gpointer user_data
972 const char *str;
974 device_fixstatus = fixstatus;
976 switch (device_fixstatus) {
977 case 1:
978 str = "NONE";
979 break;
980 case 2:
981 str = "2D";
982 break;
983 case 3:
984 str = "3D";
985 break;
986 default:
987 str = "INVALID";
988 break;
991 INFO(GYPSY_DEVICE": FixStatusChanged(fixstatus=%s)", str);
993 g_string_truncate(nmea_gga, 0);
994 g_string_truncate(nmea_gsa, 0);
995 g_string_truncate(nmea_rmc, 0);
996 g_string_truncate(nmea_gll, 0);
998 queue_send_nmea_to_clients();
999 queue_send_info_to_clients();
1002 static void
1003 time_changed(
1004 DBusGProxy *proxy,
1005 int seconds,
1006 gpointer user_data
1009 time_seconds = seconds;
1011 INFO(GYPSY_TIME": TimeChanged(seconds=%d)", time_seconds);
1013 g_string_truncate(nmea_gga, 0);
1014 g_string_truncate(nmea_rmc, 0);
1015 g_string_truncate(nmea_gll, 0);
1017 queue_send_nmea_to_clients();
1018 queue_send_info_to_clients();
1021 static void
1022 position_changed(
1023 DBusGProxy *proxy,
1024 int fields,
1025 int timestamp,
1026 double latitude,
1027 double longitude,
1028 double altitude,
1029 gpointer user_data
1032 position_fields = fields;
1033 position_timestamp = timestamp;
1034 position_latitude = latitude;
1035 position_longitude = longitude;
1036 position_altitude = altitude;
1038 INFO(GYPSY_POSITION": PositionChanged(timestamp=%d, latitude=%f, longitude=%f, altitude=%f)",
1039 position_timestamp,
1040 POSITION_LATITUDE_IS_VALID ? position_latitude : NAN,
1041 POSITION_LONGITUDE_IS_VALID ? position_longitude : NAN,
1042 POSITION_ALTITUDE_IS_VALID ? position_altitude : NAN);
1044 g_string_truncate(nmea_gga, 0);
1045 g_string_truncate(nmea_rmc, 0);
1046 g_string_truncate(nmea_gll, 0);
1048 queue_send_nmea_to_clients();
1049 queue_send_info_to_clients();
1052 static void
1053 accuracy_changed(
1054 DBusGProxy *proxy,
1055 int fields,
1056 double pdop,
1057 double hdop,
1058 double vdop,
1059 gpointer user_data
1062 accuracy_fields = fields;
1063 accuracy_pdop = pdop;
1064 accuracy_hdop = hdop;
1065 accuracy_vdop = vdop;
1067 INFO(GYPSY_ACCURACY": AccuracyChanged(pdop=%f, hdop=%f, vdop=%f)",
1068 ACCURACY_PDOP_IS_VALID ? accuracy_pdop : NAN,
1069 ACCURACY_HDOP_IS_VALID ? accuracy_hdop : NAN,
1070 ACCURACY_VDOP_IS_VALID ? accuracy_vdop : NAN);
1072 g_string_truncate(nmea_gga, 0);
1073 g_string_truncate(nmea_gsa, 0);
1075 queue_send_nmea_to_clients();
1076 queue_send_info_to_clients();
1079 static void
1080 course_changed(
1081 DBusGProxy *proxy,
1082 int fields,
1083 int timestamp,
1084 double speed,
1085 double direction,
1086 double climb,
1087 gpointer user_data
1090 course_fields = fields;
1091 course_timestamp = timestamp;
1092 course_speed = speed;
1093 course_direction = direction;
1094 course_climb = climb;
1096 INFO(GYPSY_COURSE": CourseChanged(timestamp=%d, speed=%f, direction=%f, climb=%f)",
1097 course_timestamp,
1098 COURSE_SPEED_IS_VALID ? course_speed : NAN,
1099 COURSE_DIRECTION_IS_VALID ? course_direction : NAN,
1100 COURSE_CLIMB_IS_VALID ? course_climb : NAN);
1102 g_string_truncate(nmea_rmc, 0);
1103 g_string_truncate(nmea_vtg, 0);
1105 queue_send_nmea_to_clients();
1106 queue_send_info_to_clients();
1109 static void
1110 satellites_changed(
1111 DBusGProxy *proxy,
1112 GPtrArray *satellites,
1113 gpointer user_data
1116 int i;
1118 if (!satellites) {
1119 satellite_satellitec = 0;
1120 } else {
1121 satellite_satellitec = MIN(SATELLITEC_MAX, satellites->len);
1124 for (i = 0; i < satellite_satellitec; i++) {
1125 struct satellite *sat;
1126 GValueArray *val;
1128 sat = satellite_satellitev + i;
1129 val = satellites->pdata[i];
1131 sat->prn = g_value_get_uint(g_value_array_get_nth(val, 0));
1132 sat->used = g_value_get_boolean(g_value_array_get_nth(val, 1));
1133 sat->elevation = g_value_get_uint(g_value_array_get_nth(val, 2));
1134 sat->azimuth = g_value_get_uint(g_value_array_get_nth(val, 3));
1135 sat->snr = g_value_get_uint(g_value_array_get_nth(val, 4));
1137 INFO(GYPSY_SATELLITE": SatellitesChanged(index=%d, prn=%u, used=%s, elevation=%u, azimuth=%u, snr=%u)",
1139 sat->prn,
1140 sat->used ? "TRUE" : "FALSE",
1141 sat->elevation,
1142 sat->azimuth,
1143 sat->snr);
1146 g_string_truncate(nmea_gga, 0);
1147 g_string_truncate(nmea_gsa, 0);
1148 g_string_truncate(nmea_gsv[0], 0);
1149 g_string_truncate(nmea_gsv[1], 0);
1150 g_string_truncate(nmea_gsv[2], 0);
1152 queue_send_nmea_to_clients();
1153 queue_send_sats_to_clients();
1156 /******************************************************************************/
1158 static void
1159 get_connection_status_notify(DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
1161 GError *error = NULL;
1162 gboolean connectionstatus = FALSE;
1164 DEBUG(GYPSY_DEVICE": GetConnectionStatus: Notify");
1166 dbus_g_proxy_end_call(proxy, call, &error,
1167 G_TYPE_BOOLEAN, &connectionstatus,
1168 G_TYPE_INVALID);
1170 if (error) {
1171 WARNING(GYPSY_DEVICE": GetConnectionStatus: %s", error->message);
1172 g_error_free(error);
1173 } else {
1174 DEBUG(GYPSY_DEVICE": GetConnectionStatus: OK");
1176 if (proxy == device_proxy) {
1177 connection_status_changed(NULL, connectionstatus, NULL);
1182 static void
1183 get_fix_status_notify(DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
1185 GError *error = NULL;
1186 int fixstatus = 0;
1188 DEBUG(GYPSY_DEVICE": GetFixStatus: Notify");
1190 dbus_g_proxy_end_call(proxy, call, &error,
1191 G_TYPE_INT, &fixstatus,
1192 G_TYPE_INVALID);
1194 if (error) {
1195 WARNING(GYPSY_DEVICE": GetFixStatus: %s", error->message);
1196 g_error_free(error);
1197 } else {
1198 DEBUG(GYPSY_DEVICE": GetFixStatus: OK");
1200 if (proxy == device_proxy) {
1201 fix_status_changed(NULL, fixstatus, NULL);
1206 static void
1207 get_time_notify(DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
1209 GError *error = NULL;
1210 int seconds = 0;
1212 DEBUG(GYPSY_TIME": GetTime: Notify");
1214 dbus_g_proxy_end_call(proxy, call, &error,
1215 G_TYPE_INT, &seconds,
1216 G_TYPE_INVALID);
1218 if (error) {
1219 WARNING(GYPSY_TIME": GetTime: %s", error->message);
1220 g_error_free(error);
1221 } else {
1222 DEBUG(GYPSY_TIME": GetTime: OK");
1224 if (proxy == time_proxy) {
1225 time_changed(NULL, seconds, NULL);
1230 static void
1231 get_position_notify(DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
1233 GError *error = NULL;
1234 int fields = 0, timestamp = 0;
1235 double latitude = 0.0, longitude = 0.0, altitude = 0.0;
1237 DEBUG(GYPSY_POSITION": GetPosition: Notify");
1239 dbus_g_proxy_end_call(proxy, call, &error,
1240 G_TYPE_INT, &fields,
1241 G_TYPE_INT, &timestamp,
1242 G_TYPE_DOUBLE, &latitude,
1243 G_TYPE_DOUBLE, &longitude,
1244 G_TYPE_DOUBLE, &altitude,
1245 G_TYPE_INVALID);
1247 if (error) {
1248 WARNING(GYPSY_POSITION": GetPosition: %s", error->message);
1249 g_error_free(error);
1250 } else {
1251 DEBUG(GYPSY_POSITION": GetPosition: OK");
1253 if (proxy == position_proxy) {
1254 position_changed(NULL, fields, timestamp, latitude,
1255 longitude, altitude, NULL);
1260 static void
1261 get_accuracy_notify(DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
1263 GError *error = NULL;
1264 int fields = 0;
1265 double pdop = 0.0, hdop = 0.0, vdop = 0.0;
1267 DEBUG(GYPSY_ACCURACY": GetAccuracy: Notify");
1269 dbus_g_proxy_end_call(proxy, call, &error,
1270 G_TYPE_INT, &fields,
1271 G_TYPE_DOUBLE, &pdop,
1272 G_TYPE_DOUBLE, &hdop,
1273 G_TYPE_DOUBLE, &vdop,
1274 G_TYPE_INVALID);
1276 if (error) {
1277 WARNING(GYPSY_ACCURACY": GetAccuracy: %s", error->message);
1278 g_error_free(error);
1279 } else {
1280 DEBUG(GYPSY_ACCURACY": GetAccuracy: OK");
1282 if (proxy == accuracy_proxy) {
1283 accuracy_changed(NULL, fields, pdop, hdop, vdop, NULL);
1288 static void
1289 get_course_notify(DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
1291 GError *error = NULL;
1292 int fields = 0, timestamp = 0;
1293 double speed = 0.0, direction = 0.0, climb = 0.0;
1295 DEBUG(GYPSY_COURSE": GetCourse: Notify");
1297 dbus_g_proxy_end_call(proxy, call, &error,
1298 G_TYPE_INT, &fields,
1299 G_TYPE_INT, &timestamp,
1300 G_TYPE_DOUBLE, &speed,
1301 G_TYPE_DOUBLE, &direction,
1302 G_TYPE_DOUBLE, &climb,
1303 G_TYPE_INVALID);
1305 if (error) {
1306 WARNING(GYPSY_COURSE": GetCourse: %s", error->message);
1307 g_error_free(error);
1308 } else {
1309 DEBUG(GYPSY_COURSE": GetCourse: OK");
1311 if (proxy == course_proxy) {
1312 course_changed(NULL, fields, timestamp, speed,
1313 direction, climb, NULL);
1318 static void
1319 get_satellites_notify(DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
1321 GError *error = NULL;
1322 GPtrArray *satellites = NULL;
1324 DEBUG(GYPSY_SATELLITE": GetSatellites: Notify");
1326 dbus_g_proxy_end_call(proxy, call, &error,
1327 dbus_g_type_get_collection("GPtrArray",
1328 dbus_g_type_get_struct("GValueArray",
1329 G_TYPE_UINT,
1330 G_TYPE_BOOLEAN,
1331 G_TYPE_UINT,
1332 G_TYPE_UINT,
1333 G_TYPE_UINT,
1334 G_TYPE_INVALID)),
1335 &satellites,
1336 G_TYPE_INVALID);
1338 if (error) {
1339 WARNING(GYPSY_SATELLITE": GetSatellites: %s", error->message);
1340 g_error_free(error);
1341 } else {
1342 DEBUG(GYPSY_SATELLITE": GetSatellites: OK");
1344 if (proxy == satellite_proxy) {
1345 satellites_changed(NULL, satellites, NULL);
1350 /******************************************************************************/
1352 static void
1353 device_start_notify(DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
1355 GError *error = NULL;
1357 DEBUG(GYPSY_DEVICE": Start: Notify");
1359 dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_INVALID);
1361 if (error) {
1362 WARNING(GYPSY_DEVICE": Start: %s", error->message);
1363 g_error_free(error);
1364 gypsy_device_start_failed = TRUE;
1365 } else {
1366 DEBUG(GYPSY_DEVICE": Start: OK");
1367 gypsy_device_start_failed = FALSE;
1369 if (device_proxy) {
1370 DEBUG(GYPSY_DEVICE": GetConnectionStatus");
1371 dbus_g_proxy_begin_call(device_proxy, "GetConnectionStatus",
1372 get_connection_status_notify, NULL, NULL,
1373 G_TYPE_INVALID);
1375 DEBUG(GYPSY_DEVICE": GetFixStatus");
1376 dbus_g_proxy_begin_call(device_proxy, "GetFixStatus",
1377 get_fix_status_notify, NULL, NULL,
1378 G_TYPE_INVALID);
1381 if (time_proxy) {
1382 DEBUG(GYPSY_TIME": GetTime");
1383 dbus_g_proxy_begin_call(time_proxy, "GetTime",
1384 get_time_notify, NULL, NULL,
1385 G_TYPE_INVALID);
1388 if (position_proxy) {
1389 DEBUG(GYPSY_POSITION": GetPosition");
1390 dbus_g_proxy_begin_call(position_proxy, "GetPosition",
1391 get_position_notify, NULL, NULL,
1392 G_TYPE_INVALID);
1395 if (accuracy_proxy) {
1396 DEBUG(GYPSY_ACCURACY": GetAccuracy");
1397 dbus_g_proxy_begin_call(accuracy_proxy, "GetAccuracy",
1398 get_accuracy_notify, NULL, NULL,
1399 G_TYPE_INVALID);
1402 if (course_proxy) {
1403 DEBUG(GYPSY_COURSE": GetCourse");
1404 dbus_g_proxy_begin_call(course_proxy, "GetCourse",
1405 get_course_notify, NULL, NULL,
1406 G_TYPE_INVALID);
1409 if (satellite_proxy) {
1410 DEBUG(GYPSY_SATELLITE": GetSatellites");
1411 dbus_g_proxy_begin_call(satellite_proxy, "GetSatellites",
1412 get_satellites_notify, NULL, NULL,
1413 G_TYPE_INVALID);
1418 /******************************************************************************/
1420 static void
1421 device_start(void)
1423 if (!gypsy_device_path) {
1424 WARNING("No device, not starting");
1425 return;
1428 gypsy_device_start_failed = FALSE;
1430 /* Device */
1431 device_proxy = dbus_g_proxy_new_for_name(connection,
1432 gypsy_service, gypsy_device_path, GYPSY_DEVICE);
1434 DEBUG(GYPSY_DEVICE": Start");
1435 dbus_g_proxy_begin_call(device_proxy, "Start",
1436 device_start_notify, NULL, NULL,
1437 G_TYPE_INVALID);
1439 dbus_g_proxy_add_signal(device_proxy, "ConnectionStatusChanged",
1440 G_TYPE_BOOLEAN,
1441 G_TYPE_INVALID);
1442 dbus_g_proxy_connect_signal(device_proxy, "ConnectionStatusChanged",
1443 G_CALLBACK(connection_status_changed), NULL, NULL);
1445 dbus_g_proxy_add_signal(device_proxy, "FixStatusChanged",
1446 G_TYPE_INT,
1447 G_TYPE_INVALID);
1448 dbus_g_proxy_connect_signal(device_proxy, "FixStatusChanged",
1449 G_CALLBACK(fix_status_changed), NULL, NULL);
1451 /* Time */
1452 time_proxy = dbus_g_proxy_new_for_name(connection,
1453 gypsy_service, gypsy_device_path, GYPSY_TIME);
1455 dbus_g_proxy_add_signal(time_proxy, "TimeChanged",
1456 G_TYPE_INT,
1457 G_TYPE_INVALID);
1458 dbus_g_proxy_connect_signal(time_proxy, "TimeChanged",
1459 G_CALLBACK(time_changed), NULL, NULL);
1461 /* Position */
1462 position_proxy = dbus_g_proxy_new_for_name(connection,
1463 gypsy_service, gypsy_device_path, GYPSY_POSITION);
1465 dbus_g_proxy_add_signal(position_proxy, "PositionChanged",
1466 G_TYPE_INT,
1467 G_TYPE_INT,
1468 G_TYPE_DOUBLE,
1469 G_TYPE_DOUBLE,
1470 G_TYPE_DOUBLE,
1471 G_TYPE_INVALID);
1472 dbus_g_proxy_connect_signal(position_proxy, "PositionChanged",
1473 G_CALLBACK(position_changed), NULL, NULL);
1475 /* Accuracy */
1476 accuracy_proxy = dbus_g_proxy_new_for_name(connection,
1477 gypsy_service, gypsy_device_path, GYPSY_ACCURACY);
1479 dbus_g_proxy_add_signal(accuracy_proxy, "AccuracyChanged",
1480 G_TYPE_INT,
1481 G_TYPE_DOUBLE,
1482 G_TYPE_DOUBLE,
1483 G_TYPE_DOUBLE,
1484 G_TYPE_INVALID);
1485 dbus_g_proxy_connect_signal(accuracy_proxy, "AccuracyChanged",
1486 G_CALLBACK(accuracy_changed), NULL, NULL);
1488 /* Course */
1489 course_proxy = dbus_g_proxy_new_for_name(connection,
1490 gypsy_service, gypsy_device_path, GYPSY_COURSE);
1492 dbus_g_proxy_add_signal(course_proxy, "CourseChanged",
1493 G_TYPE_INT,
1494 G_TYPE_INT,
1495 G_TYPE_DOUBLE,
1496 G_TYPE_DOUBLE,
1497 G_TYPE_DOUBLE,
1498 G_TYPE_INVALID);
1499 dbus_g_proxy_connect_signal(course_proxy, "CourseChanged",
1500 G_CALLBACK(course_changed), NULL, NULL);
1502 /* Satellites */
1503 satellite_proxy = dbus_g_proxy_new_for_name(connection,
1504 gypsy_service, gypsy_device_path, GYPSY_SATELLITE);
1506 dbus_g_proxy_add_signal(satellite_proxy, "SatellitesChanged",
1507 dbus_g_type_get_collection("GPtrArray",
1508 dbus_g_type_get_struct("GValueArray",
1509 G_TYPE_UINT,
1510 G_TYPE_BOOLEAN,
1511 G_TYPE_UINT,
1512 G_TYPE_UINT,
1513 G_TYPE_UINT,
1514 G_TYPE_INVALID)),
1515 G_TYPE_INVALID);
1516 dbus_g_proxy_connect_signal(satellite_proxy, "SatellitesChanged",
1517 G_CALLBACK(satellites_changed), NULL, NULL);
1520 /******************************************************************************/
1522 static void
1523 device_stop_notify(DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
1525 GError *error = NULL;
1527 DEBUG(GYPSY_DEVICE": Stop: Notify");
1529 dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_INVALID);
1531 if (error) {
1532 WARNING(GYPSY_DEVICE": Stop: %s", error->message);
1533 g_error_free(error);
1534 } else {
1535 DEBUG(GYPSY_DEVICE": Stop: OK");
1539 static void
1540 device_stop(void)
1542 if (!gypsy_device_path) {
1543 WARNING("No device, not stopping");
1544 return;
1547 /* Device */
1548 if (gypsy_device_start_failed) {
1549 gypsy_device_start_failed = FALSE;
1550 } else {
1551 DEBUG(GYPSY_DEVICE": Stop");
1552 dbus_g_proxy_begin_call(device_proxy, "Stop",
1553 device_stop_notify, NULL, NULL,
1554 G_TYPE_INVALID);
1557 dbus_g_proxy_disconnect_signal(device_proxy, "ConnectionStatusChanged",
1558 G_CALLBACK(connection_status_changed), NULL);
1559 dbus_g_proxy_disconnect_signal(device_proxy, "FixStatusChanged",
1560 G_CALLBACK(fix_status_changed), NULL);
1561 g_object_unref(G_OBJECT(device_proxy));
1562 device_proxy = NULL;
1563 device_connectionstatus = FALSE;
1564 device_fixstatus = 0;
1566 /* Time */
1567 dbus_g_proxy_disconnect_signal(time_proxy, "TimeChanged",
1568 G_CALLBACK(time_changed), NULL);
1569 g_object_unref(G_OBJECT(time_proxy));
1570 time_proxy = NULL;
1571 time_seconds = 0;
1573 /* Position */
1574 dbus_g_proxy_disconnect_signal(position_proxy, "PositionChanged",
1575 G_CALLBACK(position_changed), NULL);
1576 g_object_unref(G_OBJECT(position_proxy));
1577 position_proxy = NULL;
1578 position_fields = 0;
1579 position_timestamp = 0;
1580 position_latitude = 0.0;
1581 position_longitude = 0.0;
1582 position_altitude = 0.0;
1584 /* Accuracy */
1585 dbus_g_proxy_disconnect_signal(accuracy_proxy, "AccuracyChanged",
1586 G_CALLBACK(accuracy_changed), NULL);
1587 g_object_unref(G_OBJECT(accuracy_proxy));
1588 accuracy_proxy = NULL;
1589 accuracy_fields = 0;
1590 accuracy_pdop = 0.0;
1591 accuracy_hdop = 0.0;
1592 accuracy_vdop = 0.0;
1594 /* Course */
1595 dbus_g_proxy_disconnect_signal(course_proxy, "CourseChanged",
1596 G_CALLBACK(course_changed), NULL);
1597 g_object_unref(G_OBJECT(course_proxy));
1598 course_proxy = NULL;
1599 course_fields = 0;
1600 course_timestamp = 0;
1601 course_speed = 0.0;
1602 course_direction = 0.0;
1603 course_climb = 0.0;
1605 /* Satellite */
1606 dbus_g_proxy_disconnect_signal(satellite_proxy, "SatellitesChanged",
1607 G_CALLBACK(satellites_changed), NULL);
1608 g_object_unref(G_OBJECT(satellite_proxy));
1609 satellite_proxy = NULL;
1610 satellite_satellitec = 0;
1613 /******************************************************************************/
1615 static void
1616 server_create_notify(DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
1618 GError *error = NULL;
1619 const char *path = NULL;
1621 DEBUG(GYPSY_SERVER": Create: Notify");
1623 dbus_g_proxy_end_call(proxy, call, &error,
1624 DBUS_TYPE_G_OBJECT_PATH, &path,
1625 G_TYPE_INVALID);
1627 if (error) {
1628 WARNING(GYPSY_SERVER": Create: %s", error->message);
1629 g_error_free(error);
1630 gypsy_device_create_failed = TRUE;
1631 gypsy_device_path = NULL;
1632 } else {
1633 DEBUG(GYPSY_SERVER": Create: OK: %s", path);
1634 gypsy_device_create_failed = FALSE;
1635 gypsy_device_path = g_strdup(path);
1637 if (client_list || nowait) {
1638 device_start();
1643 static void
1644 server_create(void)
1646 gypsy_device_create_failed = FALSE;
1648 DEBUG(GYPSY_SERVER": Create");
1649 dbus_g_proxy_begin_call(server_proxy, "Create",
1650 server_create_notify, NULL, NULL,
1651 G_TYPE_STRING, gypsy_device,
1652 G_TYPE_INVALID);
1655 /******************************************************************************/
1657 static void
1658 name_owner_changed(
1659 DBusGProxy *proxy,
1660 const char *name,
1661 const char *prev,
1662 const char *new,
1663 gpointer user_data
1666 INFO("org.freedesktop.DBus: NameOwnerChanged(name=%s, prev=%s, new=%s)",
1667 name, prev, new);
1669 if (name && !strcmp(name, gypsy_service)) {
1670 if (prev && strlen(prev) > 0) {
1671 g_free(gypsy_device_path);
1673 if (new && strlen(new) > 0) {
1674 server_create();
1676 gypsy_device_path = NULL;
1680 /******************************************************************************/
1682 static gboolean
1683 timeout_handler(gpointer data)
1685 if (!client_list && !nowait) {
1686 DEBUG("Timeout: no client: removing timeout handler");
1687 timeout_handler_registered = FALSE;
1688 return FALSE;
1689 } else {
1690 if (gypsy_device_create_failed) {
1691 WARNING("Timeout: no gypsy device created");
1692 server_create();
1693 } else if (!gypsy_device_path) {
1694 WARNING("Timeout: no gypsy device available");
1695 } else if (gypsy_device_start_failed) {
1696 WARNING("Timeout: gypsy device not started");
1697 DEBUG(GYPSY_DEVICE": Start");
1698 dbus_g_proxy_begin_call(device_proxy, "Start",
1699 device_start_notify, NULL, NULL,
1700 G_TYPE_INVALID);
1701 } else if (!device_connectionstatus) {
1702 WARNING("Timeout: device started but not connected");
1703 } else {
1704 GTimeVal tv;
1706 DEBUG("Timeout: sending data to clients");
1708 g_get_current_time(&tv);
1710 send_info_to_clients(&tv);
1711 send_sats_to_clients(&tv);
1712 send_nmea_to_clients(&tv);
1714 return TRUE;
1718 /******************************************************************************/
1720 static void
1721 marshal_VOID__STRING_STRING_STRING(
1722 GClosure *closure,
1723 GValue *return_value G_GNUC_UNUSED,
1724 guint n_param_values,
1725 const GValue *param_values,
1726 gpointer invocation_hint G_GNUC_UNUSED,
1727 gpointer marshal_data
1730 typedef void (*GMarshalFunc_VOID__STRING_STRING_STRING)(
1731 gpointer data1,
1732 const gchar *arg_1,
1733 const gchar *arg_2,
1734 const gchar *arg_3,
1735 gpointer data2);
1737 GMarshalFunc_VOID__STRING_STRING_STRING callback;
1738 GCClosure *cc = (GCClosure*) closure;
1739 gpointer data1, data2;
1741 g_return_if_fail (n_param_values == 4);
1743 if (G_CCLOSURE_SWAP_DATA(closure)) {
1744 data1 = closure->data;
1745 data2 = g_value_peek_pointer(param_values + 0);
1746 } else {
1747 data1 = g_value_peek_pointer(param_values + 0);
1748 data2 = closure->data;
1751 callback = (GMarshalFunc_VOID__STRING_STRING_STRING) (
1752 marshal_data ? marshal_data : cc->callback);
1754 callback(data1,
1755 (const gchar *) g_value_get_string(param_values + 1),
1756 (const gchar *) g_value_get_string(param_values + 2),
1757 (const gchar *) g_value_get_string(param_values + 3),
1758 data2);
1761 static void
1762 marshal_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE(
1763 GClosure *closure,
1764 GValue *return_value G_GNUC_UNUSED,
1765 guint n_param_values,
1766 const GValue *param_values,
1767 gpointer invocation_hint G_GNUC_UNUSED,
1768 gpointer marshal_data
1771 typedef void (*GMarshalFunc_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE)(
1772 gpointer data1,
1773 gint arg_1,
1774 gint arg_2,
1775 gdouble arg_3,
1776 gdouble arg_4,
1777 gdouble arg_5,
1778 gpointer data2);
1780 GMarshalFunc_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE callback;
1781 GCClosure *cc = (GCClosure*) closure;
1782 gpointer data1, data2;
1784 g_return_if_fail(n_param_values == 6);
1786 if (G_CCLOSURE_SWAP_DATA(closure)) {
1787 data1 = closure->data;
1788 data2 = g_value_peek_pointer(param_values + 0);
1789 } else {
1790 data1 = g_value_peek_pointer(param_values + 0);
1791 data2 = closure->data;
1794 callback = (GMarshalFunc_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE) (
1795 marshal_data ? marshal_data : cc->callback);
1797 callback(data1,
1798 g_value_get_int(param_values + 1),
1799 g_value_get_int(param_values + 2),
1800 g_value_get_double(param_values + 3),
1801 g_value_get_double(param_values + 4),
1802 g_value_get_double(param_values + 5),
1803 data2);
1806 static void
1807 marshal_VOID__INT_DOUBLE_DOUBLE_DOUBLE(
1808 GClosure *closure,
1809 GValue *return_value G_GNUC_UNUSED,
1810 guint n_param_values,
1811 const GValue *param_values,
1812 gpointer invocation_hint G_GNUC_UNUSED,
1813 gpointer marshal_data
1816 typedef void (*GMarshalFunc_VOID__INT_DOUBLE_DOUBLE_DOUBLE)(
1817 gpointer data1,
1818 gint arg_1,
1819 gdouble arg_2,
1820 gdouble arg_3,
1821 gdouble arg_4,
1822 gpointer data2);
1824 GMarshalFunc_VOID__INT_DOUBLE_DOUBLE_DOUBLE callback;
1825 GCClosure *cc = (GCClosure*) closure;
1826 gpointer data1, data2;
1828 g_return_if_fail(n_param_values == 5);
1830 if (G_CCLOSURE_SWAP_DATA(closure)) {
1831 data1 = closure->data;
1832 data2 = g_value_peek_pointer(param_values + 0);
1833 } else {
1834 data1 = g_value_peek_pointer(param_values + 0);
1835 data2 = closure->data;
1838 callback = (GMarshalFunc_VOID__INT_DOUBLE_DOUBLE_DOUBLE) (
1839 marshal_data ? marshal_data : cc->callback);
1841 callback(data1,
1842 g_value_get_int(param_values + 1),
1843 g_value_get_double(param_values + 2),
1844 g_value_get_double(param_values + 3),
1845 g_value_get_double(param_values + 4),
1846 data2);
1849 /******************************************************************************/
1851 static gboolean
1852 read_from_client(GIOChannel *source, GIOCondition condition, gpointer data)
1854 struct client *client;
1855 char buf[4096];
1856 gsize len = sizeof(buf);
1857 GIOStatus status;
1858 GString *reply;
1859 char *p;
1861 client = data;
1863 if (condition != G_IO_IN) {
1864 shutdown_client(client);
1867 if (!client->channel) {
1868 MESSAGE("Removing client");
1870 client_list = g_list_remove(client_list, client);
1871 g_free(client);
1872 g_io_channel_unref(source);
1874 if (!client_list && !nowait) {
1875 device_stop();
1878 return FALSE;
1881 status = g_io_channel_read_chars(source, buf, len - 2, &len, NULL);
1883 if ((status != G_IO_STATUS_NORMAL) || (len < 1)) {
1884 shutdown_client(client);
1885 return TRUE;
1888 if (buf[len - 1] != '\n') {
1889 buf[len] = '\n';
1890 len++;
1893 buf[len] = '\0';
1895 reply = g_string_sized_new(128);
1896 g_string_assign(reply, "GPSD");
1898 p = buf;
1900 while (*p) {
1901 switch (toupper(*p++)) {
1902 case 'A':
1903 if (!POSITION_ALTITUDE_IS_VALID) {
1904 g_string_append(reply, ",A=?");
1905 } else {
1906 g_string_append_printf(reply, ",A=%.3f",
1907 position_altitude);
1909 break;
1910 case 'B':
1911 g_string_append(reply, ",B=?");
1912 break;
1913 case 'C':
1914 g_string_append(reply, ",C=1.00"); /* FIXME */
1915 break;
1916 case 'D':
1917 if (time_seconds <= 0) {
1918 g_string_append(reply, ",D=?");
1919 } else {
1920 struct tm *tm;
1921 time_t seconds;
1923 seconds = time_seconds;
1924 tm = gmtime(&seconds);
1926 g_string_append_printf(reply,
1927 ",D=%04d-%02d-%02dT%02d:%02d:%02d.00Z",
1928 tm->tm_year + 1900, tm->tm_mon + 1,
1929 tm->tm_mday, tm->tm_hour, tm->tm_min,
1930 tm->tm_sec);
1932 break;
1933 case 'E':
1934 g_string_append(reply, ",E=");
1936 if (!ACCURACY_PDOP_IS_VALID) {
1937 g_string_append(reply, "?");
1938 } else {
1939 g_string_append_printf(reply, "%.2f",
1940 accuracy_pdop);
1942 if (!ACCURACY_HDOP_IS_VALID) {
1943 g_string_append(reply, " ?");
1944 } else {
1945 g_string_append_printf(reply, " %.2f",
1946 accuracy_hdop);
1948 if (!ACCURACY_VDOP_IS_VALID) {
1949 g_string_append(reply, " ?");
1950 } else {
1951 g_string_append_printf(reply, " %.2f",
1952 accuracy_vdop);
1954 break;
1955 case 'F':
1956 if (*p == '=') {
1957 while (isprint(*p) && !isspace(*p)) {
1958 p++;
1961 g_string_append(reply, ",F=gypsy");
1962 break;
1963 case 'G':
1964 if (*p == '=') {
1965 p += strcspn(p, ",\r\n");
1967 g_string_append(reply, ",G=GPS");
1968 break;
1969 case 'I':
1970 g_string_append(reply, ",I=gypsy");
1971 break;
1972 case 'J':
1973 if (*p == '=') {
1974 p++;
1976 if (*p == '1' || *p == '+') {
1977 p++;
1978 g_string_append(reply, ",J=1");
1979 } else {
1980 if (*p == '0' || *p == '-') {
1981 p++;
1983 g_string_append(reply, ",J=0");
1985 break;
1986 case 'K':
1987 g_string_append(reply, ",K=1 gypsy");
1988 break;
1989 case 'L':
1990 g_string_append(reply, ",L=3 " VERSION
1991 " abcdefgijklmnopqrstuvwxyz");
1992 break;
1993 case 'M':
1994 g_string_append_printf(reply, ",M=%d",
1995 get_fixstatus());
1996 break;
1997 case 'N':
1998 g_string_append(reply, ",N=0");
1999 break;
2000 case 'O':
2001 add_info(reply);
2002 break;
2003 case 'P':
2004 if (!POSITION_LATITUDE_IS_VALID
2005 || !POSITION_LONGITUDE_IS_VALID) {
2006 g_string_append(reply, ",P=?");
2007 } else {
2008 g_string_append_printf(reply, ",P=%.6f %.6f",
2009 position_latitude, position_longitude);
2011 break;
2012 case 'Q':
2013 if (!ACCURACY_PDOP_IS_VALID
2014 && !ACCURACY_HDOP_IS_VALID
2015 && !ACCURACY_VDOP_IS_VALID) {
2016 g_string_append(reply, ",Q=?");
2017 } else {
2018 unsigned i, used;
2020 for (i = 0, used = 0; i < satellite_satellitec; i++) {
2021 if (satellite_satellitev[i].used) {
2022 used++;
2026 g_string_append_printf(reply,
2027 ",Q=%d %.2f %.2f %.2f %.2f %.2f", used,
2028 ACCURACY_PDOP_IS_VALID ? accuracy_pdop : 0.0,
2029 ACCURACY_HDOP_IS_VALID ? accuracy_hdop : 0.0,
2030 ACCURACY_VDOP_IS_VALID ? accuracy_vdop : 0.0,
2031 0.0, 0.0);
2033 break;
2034 case 'R':
2035 if (*p == '=') {
2036 p++;
2038 if (*p == '2' || *p == '1' || *p == '+') {
2039 client->raw = TRUE;
2040 p++;
2041 } else if (*p == '0' || *p == '-') {
2042 client->raw = FALSE;
2043 p++;
2044 } else {
2045 client->raw = !client->raw;
2047 g_string_append_printf(reply, ",R=%1d", client->raw);
2048 break;
2049 case 'S':
2050 g_string_append_printf(reply, ",S=%d",
2051 MAX(get_fixstatus() - 1, 0));
2052 break;
2053 case 'T':
2054 if (!COURSE_DIRECTION_IS_VALID) {
2055 g_string_append(reply, ",T=?");
2056 } else {
2057 g_string_append_printf(reply, ",T=%.4f",
2058 course_direction);
2060 break;
2061 case 'U':
2062 if (!COURSE_CLIMB_IS_VALID) {
2063 g_string_append(reply, ",U=?");
2064 } else {
2065 g_string_append_printf(reply, ",U=%.3f",
2066 course_climb);
2068 break;
2069 case 'V':
2070 if (!COURSE_SPEED_IS_VALID) {
2071 g_string_append(reply, ",V=?");
2072 } else {
2073 g_string_append_printf(reply, ",V=%.3f",
2074 course_speed * KNOTS_TO_MPS);
2076 break;
2077 case 'W':
2078 if (*p == '=') {
2079 p++;
2081 if (*p == '1' || *p == '+') {
2082 client->watcher = TRUE;
2083 p++;
2084 } else if (*p == '0' || *p == '-') {
2085 client->watcher = FALSE;
2086 p++;
2087 } else {
2088 client->watcher = !client->watcher;
2090 g_string_append_printf(reply, ",W=%1d", client->watcher);
2091 break;
2092 case 'X':
2093 if (time_seconds <= 0) {
2094 g_string_append(reply, ",X=?");
2095 } else {
2096 g_string_append_printf(reply, ",X=%d.000000",
2097 time_seconds);
2099 break;
2100 case 'Y':
2101 add_sats(reply);
2102 break;
2103 case 'Z':
2104 g_string_append(reply, ",Z=?"); /* FIXME */
2105 break;
2106 case '$':
2107 g_string_append(reply, ",$=?"); /* FIXME */
2108 break;
2109 case '\r':
2110 case '\n':
2111 goto finish;
2112 default:
2113 break;
2117 finish:
2118 g_string_append(reply, "\r\n");
2119 send_to_client(client, reply->str, reply->len);
2120 g_string_free(reply, TRUE);
2121 return TRUE;
2124 static gboolean
2125 accept_new_client(GIOChannel *source, GIOCondition condition, gpointer data)
2127 struct sockaddr_in addr;
2128 socklen_t len = sizeof(addr);
2129 struct client *client;
2130 int fd;
2132 MESSAGE("New client request");
2134 fd = accept(g_io_channel_unix_get_fd(source),
2135 (struct sockaddr *) &addr, &len);
2137 if (fd < 0) {
2138 ERROR("Cannot accept new connection: %s", strerror(errno));
2141 client = g_new0(struct client, 1);
2143 if (!client) {
2144 ERROR("Cannot allocate memory");
2147 if (!timeout_handler_registered) {
2148 timeout_handler_registered = TRUE;
2149 g_timeout_add(2000, timeout_handler, NULL);
2152 if (!client_list && !nowait) {
2153 device_start();
2156 client_list = g_list_prepend(client_list, client);
2158 client->channel = g_io_channel_unix_new(fd);
2160 g_io_channel_set_flags(client->channel, G_IO_FLAG_NONBLOCK, NULL);
2161 g_io_channel_set_encoding(client->channel, NULL, NULL);
2162 g_io_channel_set_buffered(client->channel, FALSE);
2164 g_io_add_watch(client->channel,
2165 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
2166 read_from_client, client);
2168 return TRUE;
2171 static void
2172 init_tcp_service(const char *address, const char *port)
2174 struct addrinfo hints, *l, *list = NULL;
2175 int error, errno_backup = 0;
2176 gboolean ok = FALSE;
2178 memset(&hints, 0, sizeof(hints));
2179 hints.ai_family = AF_UNSPEC;
2180 hints.ai_socktype = SOCK_STREAM;
2181 hints.ai_flags = AI_PASSIVE;
2183 if (address && !strlen(address)) {
2184 address = NULL;
2187 if (!port || !strlen(port)) {
2188 port = "2947";
2191 error = getaddrinfo(address, port, &hints, &list);
2193 if (error) {
2194 if (address) {
2195 ERROR("Cannot get address info for address=%s, port=%s: %s",
2196 address, port, gai_strerror(error));
2197 } else {
2198 ERROR("Cannot get address info for port=%s: %s",
2199 port, gai_strerror(error));
2203 for (l = list; l; l = l->ai_next) {
2204 GIOChannel *channel;
2205 int fd, one = 1;
2207 fd = socket(l->ai_family, l->ai_socktype, l->ai_protocol);
2209 if (fd < 0) {
2210 errno_backup = errno;
2211 continue;
2214 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
2215 (void *) &one, sizeof(one));
2217 if (bind(fd, l->ai_addr, l->ai_addrlen) < 0) {
2218 errno_backup = errno;
2219 close(fd);
2220 continue;
2223 if (listen(fd, 4) < 0) {
2224 errno_backup = errno;
2225 close(fd);
2226 continue;
2229 channel = g_io_channel_unix_new(fd);
2231 g_io_channel_set_encoding(channel, NULL, NULL);
2232 g_io_channel_set_buffered(channel, FALSE);
2234 g_io_add_watch(channel, G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR,
2235 accept_new_client, NULL);
2237 ok = TRUE;
2240 if (!ok) {
2241 if (address) {
2242 ERROR("Cannot listen on address=%s, port=%s: %s",
2243 address, port, strerror(errno_backup));
2244 } else {
2245 ERROR("Cannot listen on port=%s: %s",
2246 port, strerror(errno_backup));
2250 if (list) {
2251 freeaddrinfo(list);
2255 /******************************************************************************/
2257 static void
2258 printerr_handler(const char *message)
2260 if (background) {
2261 syslog(LOG_USER | LOG_INFO, "%s", message);
2262 } else {
2263 fputs(message, stderr);
2267 static void
2268 log_handler(
2269 const char *domain,
2270 GLogLevelFlags level,
2271 const char *message,
2272 gpointer user_data
2275 const char *level_string;
2277 switch (level & G_LOG_LEVEL_MASK) {
2278 case G_LOG_LEVEL_ERROR:
2279 level_string = "ERROR: ";
2280 break;
2281 case G_LOG_LEVEL_CRITICAL:
2282 level_string = "CRITICAL: ";
2283 break;
2284 case G_LOG_LEVEL_WARNING:
2285 level_string = "WARNING: ";
2286 break;
2287 case G_LOG_LEVEL_MESSAGE:
2288 level_string = "MESSAGE: ";
2289 break;
2290 case G_LOG_LEVEL_INFO:
2291 level_string = "INFO: ";
2292 break;
2293 case G_LOG_LEVEL_DEBUG:
2294 level_string = "DEBUG: ";
2295 break;
2296 default:
2297 level_string = "";
2298 break;
2301 if (background) {
2302 if (domain) {
2303 syslog(LOG_USER | LOG_INFO, "%s: %s%s", domain,
2304 level_string, message);
2305 } else {
2306 syslog(LOG_USER | LOG_INFO, "%s%s",
2307 level_string, message);
2309 } else {
2310 struct timeval tv = { 0, 0 };
2311 struct tm *tm;
2312 char buf[64];
2313 size_t len;
2315 gettimeofday(&tv, NULL);
2316 tm = localtime(&tv.tv_sec);
2317 len = strftime(buf, sizeof(buf), "%T", tm);
2319 if (!len) {
2320 *buf = '\0';
2323 if (domain) {
2324 fprintf(stderr, "%s.%06li %s: %s%s\n", buf,
2325 (long) tv.tv_usec, domain, level_string,
2326 message);
2327 } else {
2328 fprintf(stderr, "%s.%06li %s%s\n", buf,
2329 (long) tv.tv_usec, level_string, message);
2334 /******************************************************************************/
2336 static void
2337 usage(void)
2339 printf("\
2340 Usage: "PACKAGE_NAME" [-n] [-N] [-f device] [-P pidfile] [-D n] [-S port] [-h] [-V]\n\
2342 Options:\n\
2343 -n Don't wait for client connects to poll GPS\n\
2344 -N Don't go into background\n\
2345 -f device Set a GPS device name\n\
2346 -P pidfile Set file to record process ID\n\
2347 -D integer (default 0) Set debug level\n\
2348 -S integer (default 2947) Set port for daemon\n\
2349 -h Display help message and terminate\n\
2350 -V Emit version and exit\n");
2353 /******************************************************************************/
2356 main(int argc, char **argv)
2358 DBusGProxy *proxy_dbus = NULL;
2359 GMainLoop *loop = NULL;
2360 char *port = NULL;
2361 char *address = NULL;
2362 char *pid_file = NULL;
2363 gboolean foreground = FALSE;
2364 GError *error = NULL;
2366 g_set_prgname(PACKAGE_NAME);
2367 g_set_printerr_handler(printerr_handler);
2368 g_log_set_default_handler(log_handler, NULL);
2370 opterr = 0;
2371 while (1) {
2372 int option;
2374 option = getopt(argc, argv, "F:D:S:bhNnP:VR:f:");
2376 if (option == -1) {
2377 break;
2380 switch (option) {
2381 case 'F':
2382 case 'b':
2383 case 'R':
2384 WARNING("Not implemented: -%c", option);
2385 break;
2386 case 'D':
2387 switch (atoi(optarg)) {
2388 case 0:
2389 log_level = G_LOG_LEVEL_ERROR
2390 | G_LOG_LEVEL_CRITICAL
2391 | G_LOG_LEVEL_WARNING;
2392 break;
2393 case 1:
2394 log_level = G_LOG_LEVEL_ERROR
2395 | G_LOG_LEVEL_CRITICAL
2396 | G_LOG_LEVEL_WARNING
2397 | G_LOG_LEVEL_MESSAGE;
2398 break;
2399 case 2:
2400 log_level = G_LOG_LEVEL_ERROR
2401 | G_LOG_LEVEL_CRITICAL
2402 | G_LOG_LEVEL_WARNING
2403 | G_LOG_LEVEL_MESSAGE
2404 | G_LOG_LEVEL_INFO;
2405 break;
2406 default:
2407 log_level = G_LOG_LEVEL_ERROR
2408 | G_LOG_LEVEL_CRITICAL
2409 | G_LOG_LEVEL_WARNING
2410 | G_LOG_LEVEL_MESSAGE
2411 | G_LOG_LEVEL_INFO
2412 | G_LOG_LEVEL_DEBUG;
2413 break;
2415 break;
2416 case 'f':
2417 gypsy_device = optarg;
2418 break;
2419 case 'N':
2420 foreground = TRUE;
2421 break;
2422 case 'S':
2423 port = strrchr(optarg, ':');
2424 if (!port) {
2425 port = optarg;
2426 address = NULL;
2427 } else {
2428 port[0] = '\0';
2429 port++;
2430 address = optarg;
2432 break;
2433 case 'n':
2434 nowait = TRUE;
2435 break;
2436 case 'P':
2437 pid_file = optarg;
2438 break;
2439 case 'V':
2440 printf(PACKAGE_NAME" "VERSION"\n");
2441 exit(EXIT_SUCCESS);
2442 break;
2443 case 'h':
2444 case '?':
2445 default:
2446 usage();
2447 exit(EXIT_SUCCESS);
2448 break;
2452 if (!foreground) {
2453 pid_t pid;
2454 int fd;
2456 pid = fork();
2458 if (pid < 0) {
2459 ERROR("Cannot fork: %s", strerror(errno));
2460 } else if (pid > 0) {
2461 exit(EXIT_SUCCESS);
2464 openlog(PACKAGE_NAME, LOG_PID, LOG_USER);
2465 background = TRUE;
2467 setsid();
2469 if (chdir("/")) {
2470 ERROR("Cannot chdir: /: %s", strerror(errno));
2473 fd = open("/dev/null", O_RDWR, 0);
2475 if (fd < 0) {
2476 ERROR("Cannot open: /dev/null: %s", strerror(errno));
2479 dup2(fd, STDIN_FILENO);
2480 dup2(fd, STDOUT_FILENO);
2481 dup2(fd, STDERR_FILENO);
2483 close(fd);
2487 char *s;
2489 s = getenv("FSO_GPSD_GYPSY_SERVICE");
2491 if (s) {
2492 WARNING("Found environment variable: "
2493 "FSO_GPSD_GYPSY_SERVICE = %s", s);
2494 gypsy_service = g_strdup(s);
2497 s = getenv("FSO_GPSD_GYPSY_SERVER_PATH");
2499 if (s) {
2500 WARNING("Found environment variable: "
2501 "FSO_GPSD_GYPSY_SERVER_PATH = %s", s);
2502 gypsy_server_path = g_strdup(s);
2506 if (pid_file) {
2507 FILE *fp;
2509 fp = fopen(pid_file, "w");
2511 if (!fp) {
2512 ERROR("Cannot fopen: %s: %s", pid_file, strerror(errno));
2515 fprintf(fp, "%u\n", (unsigned) getpid());
2516 fclose(fp);
2519 g_type_init();
2521 loop = g_main_loop_new(NULL, FALSE);
2523 connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
2525 if (!connection) {
2526 ERROR("Failed to open connection to system bus: %s",
2527 error->message);
2530 dbus_g_object_register_marshaller(
2531 marshal_VOID__STRING_STRING_STRING,
2532 G_TYPE_NONE,
2533 G_TYPE_STRING,
2534 G_TYPE_STRING,
2535 G_TYPE_STRING,
2536 G_TYPE_INVALID);
2537 dbus_g_object_register_marshaller(
2538 marshal_VOID__INT_DOUBLE_DOUBLE_DOUBLE,
2539 G_TYPE_NONE,
2540 G_TYPE_INT,
2541 G_TYPE_DOUBLE,
2542 G_TYPE_DOUBLE,
2543 G_TYPE_DOUBLE,
2544 G_TYPE_INVALID);
2545 dbus_g_object_register_marshaller(
2546 marshal_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE,
2547 G_TYPE_NONE,
2548 G_TYPE_INT,
2549 G_TYPE_INT,
2550 G_TYPE_DOUBLE,
2551 G_TYPE_DOUBLE,
2552 G_TYPE_DOUBLE,
2553 G_TYPE_INVALID);
2555 proxy_dbus = dbus_g_proxy_new_for_name(connection,
2556 "org.freedesktop.DBus", "/org/freedesktop/DBus",
2557 "org.freedesktop.DBus");
2559 server_proxy = dbus_g_proxy_new_for_name(connection,
2560 gypsy_service, gypsy_server_path, GYPSY_SERVER);
2562 dbus_g_proxy_add_signal(proxy_dbus, "NameOwnerChanged",
2563 G_TYPE_STRING,
2564 G_TYPE_STRING,
2565 G_TYPE_STRING,
2566 G_TYPE_INVALID);
2567 dbus_g_proxy_connect_signal(proxy_dbus, "NameOwnerChanged",
2568 G_CALLBACK(name_owner_changed), NULL, NULL);
2570 server_create();
2572 nmea_gga = g_string_sized_new(128);
2573 nmea_gsa = g_string_sized_new(128);
2574 nmea_gsv[0] = g_string_sized_new(128);
2575 nmea_gsv[1] = g_string_sized_new(128);
2576 nmea_gsv[2] = g_string_sized_new(128);
2577 nmea_rmc = g_string_sized_new(128);
2578 nmea_gll = g_string_sized_new(128);
2579 nmea_vtg = g_string_sized_new(128);
2581 init_tcp_service(address, port);
2583 if (nowait) {
2584 timeout_handler_registered = TRUE;
2585 g_timeout_add(2000, timeout_handler, NULL);
2588 g_main_loop_run(loop);
2590 return 0;