"Initial commit to Gerrit"
[profile/ivi/gpsd.git] / gpspipe.c
1 /*
2  * gpspipe
3  *
4  * a simple program to connect to a gpsd daemon and dump the received data
5  * to stdout
6  *
7  * This will dump the raw NMEA from gpsd to stdout
8  *      gpspipe -r
9  *
10  * This will dump the super-raw data (gps binary) from gpsd to stdout
11  *      gpspipe -R
12  *
13  * This will dump the GPSD sentences from gpsd to stdout
14  *      gpspipe -w
15  *
16  * This will dump the GPSD and the NMEA sentences from gpsd to stdout
17  *      gpspipe -wr
18  *
19  * Original code by: Gary E. Miller <gem@rellim.com>.  Cleanup by ESR.
20  *
21  * This file is Copyright (c) 2010 by the GPSD project
22  * BSD terms apply: see the file COPYING in the distribution root for details.
23  *
24  */
25
26 #include <stdlib.h>
27 #include <time.h>
28 #include "gpsd_config.h"
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #ifndef S_SPLINT_S
32 #if HAVE_SYS_SOCKET_H
33 #include <sys/socket.h>
34 #endif /* HAVE_SYS_SOCKET_H */
35 #include <unistd.h>
36 #endif /* S_SPLINT_S */
37 #include <errno.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdbool.h>
41 #include <fcntl.h>
42 #if HAVE_TERMIOS
43 #include <termios.h>
44 #endif /* HAVE_TERMIOS */
45 #include <assert.h>
46 #include "gpsd.h"
47 #include "gpsdclient.h"
48 #include "revision.h"
49
50 static struct gps_data_t gpsdata;
51 static void spinner(unsigned int, unsigned int);
52
53 /* NMEA-0183 standard baud rate */
54 #define BAUDRATE B4800
55
56 /* Serial port variables */
57 static struct termios oldtio, newtio;
58 static int fd_out = 1;          /* output initially goes to standard output */
59 static char serbuf[255];
60 static int debug;
61
62 static void daemonize(void)
63 /* Daemonize me. */
64 {
65     int i;
66     pid_t pid;
67
68     /* Run as my child. */
69     pid = fork();
70     if (pid == -1)
71         exit(1);                /* fork error */
72     if (pid > 0)
73         exit(0);                /* parent exits */
74
75     /* Obtain a new process group. */
76     (void)setsid();
77
78     /* Close all open descriptors. */
79     for (i = getdtablesize(); i >= 0; --i)
80         (void)close(i);
81
82     /* Reopen STDIN, STDOUT, STDERR to /dev/null. */
83     i = open("/dev/null", O_RDWR);      /* STDIN */
84     /*@ -sefparams @*/
85     assert(dup(i) != -1);       /* STDOUT */
86     assert(dup(i) != -1);       /* STDERR */
87
88     /* Know thy mask. */
89     (void)umask(0x033);
90
91     /* Run from a known spot. */
92     assert(chdir("/") != -1);
93     /*@ +sefparams @*/
94
95     /* Catch child sig */
96     (void)signal(SIGCHLD, SIG_IGN);
97
98     /* Ignore tty signals */
99     (void)signal(SIGTSTP, SIG_IGN);
100     (void)signal(SIGTTOU, SIG_IGN);
101     (void)signal(SIGTTIN, SIG_IGN);
102 }
103
104 static void open_serial(char *device)
105 /* open the serial port and set it up */
106 {
107     /* 
108      * Open modem device for reading and writing and not as controlling
109      * tty.
110      */
111     if ((fd_out = open(device, O_RDWR | O_NOCTTY)) == -1) {
112         fprintf(stderr, "gpspipe: error opening serial port\n");
113         exit(1);
114     }
115
116     /* Save current serial port settings for later */
117     if (tcgetattr(fd_out, &oldtio) != 0) {
118         fprintf(stderr, "gpspipe: error reading serial port settings\n");
119         exit(1);
120     }
121
122     /* Clear struct for new port settings. */
123     /*@i@*/ bzero(&newtio, sizeof(newtio));
124
125     /* make it raw */
126     (void)cfmakeraw(&newtio);
127     /* set speed */
128     /*@i@*/ (void)cfsetospeed(&newtio, BAUDRATE);
129
130     /* Clear the modem line and activate the settings for the port. */
131     (void)tcflush(fd_out, TCIFLUSH);
132     if (tcsetattr(fd_out, TCSANOW, &newtio) != 0) {
133         (void)fprintf(stderr, "gpspipe: error configuring serial port\n");
134         exit(1);
135     }
136 }
137
138 static void usage(void)
139 {
140     (void)fprintf(stderr,
141                   "Usage: gpspipe [OPTIONS] [server[:port[:device]]]\n\n"
142                   "-d Run as a daemon.\n" "-f [file] Write output to file.\n"
143                   "-h Show this help.\n" "-r Dump raw NMEA.\n"
144                   "-R Dump super-raw mode (GPS binary).\n"
145                   "-w Dump gpsd native data.\n"
146                   "-l Sleep for ten seconds before connecting to gpsd.\n"
147                   "-t Time stamp the data.\n"
148                   "-T [format] set the timestamp format (strftime(3)-like; implies '-t')\n"
149                   "-s [serial dev] emulate a 4800bps NMEA GPS on serial port (use with '-r').\n"
150                   "-n [count] exit after count packets.\n"
151                   "-v Print a little spinner.\n"
152                   "-V Print version and exit.\n\n"
153                   "You must specify one, or both, of -r/-w.\n"
154                   "You must use -f if you use -d.\n");
155 }
156
157 /*@ -compdestroy @*/
158 int main(int argc, char **argv)
159 {
160     char buf[4096];
161     bool timestamp = false;
162     char *format = "%c";
163     char tmstr[200];
164     bool daemon = false;
165     bool binary = false;
166     bool sleepy = false;
167     bool new_line = true;
168     bool raw = false;
169     bool watch = false;
170     long count = -1;
171     int option;
172     unsigned int vflag = 0, l = 0;
173     FILE *fp;
174     unsigned int flags;
175
176     struct fixsource_t source;
177     char *serialport = NULL;
178     char *outfile = NULL;
179
180     /*@-branchstate@*/
181     flags = WATCH_ENABLE;
182     while ((option = getopt(argc, argv, "?dD:lhrRwtT:vVn:s:o:")) != -1) {
183         switch (option) {
184         case 'D':
185             debug = atoi(optarg);
186 #ifdef CLIENTDEBUG_ENABLE
187             gps_enable_debug(debug, stderr);
188 #endif /* CLIENTDEBUG_ENABLE */
189             break;
190         case 'n':
191             count = strtol(optarg, 0, 0);
192             break;
193         case 'r':
194             raw = true;
195             /* 
196              * Yes, -r invokes NMEA mode rather than proper raw mode.
197              * This emulates the behavior under the old protocol.
198              */
199             flags |= WATCH_NMEA;
200             break;
201         case 'R':
202             flags |= WATCH_RAW;
203             binary = true;
204             break;
205         case 'd':
206             daemon = true;
207             break;
208         case 'l':
209             sleepy = true;
210             break;
211         case 't':
212             timestamp = true;
213             break;
214         case 'T':
215             timestamp = true;
216             format = optarg;
217             break;
218         case 'v':
219             vflag++;
220             break;
221         case 'w':
222             flags |= WATCH_JSON;
223             watch = true;
224             break;
225         case 'V':
226             (void)fprintf(stderr, "%s: %s (revision %s)\n",
227                           argv[0], VERSION, REVISION);
228             exit(0);
229         case 's':
230             serialport = optarg;
231             break;
232         case 'o':
233             outfile = optarg;
234             break;
235         case '?':
236         case 'h':
237         default:
238             usage();
239             exit(1);
240         }
241     }
242     /*@+branchstate@*/
243
244     /* Grok the server, port, and device. */
245     if (optind < argc) {
246         gpsd_source_spec(argv[optind], &source);
247     } else
248         gpsd_source_spec(NULL, &source);
249
250     if (serialport != NULL && !raw) {
251         (void)fprintf(stderr, "gpspipe: use of '-s' requires '-r'.\n");
252         exit(1);
253     }
254
255     if (outfile == NULL && daemon) {
256         (void)fprintf(stderr, "gpspipe: use of '-d' requires '-f'.\n");
257         exit(1);
258     }
259
260     if (!raw && !watch && !binary) {
261         (void)fprintf(stderr,
262                       "gpspipe: one of '-R', '-r' or '-w' is required.\n");
263         exit(1);
264     }
265
266     /* Daemonize if the user requested it. */
267     if (daemon)
268         daemonize();
269
270     /* Sleep for ten seconds if the user requested it. */
271     if (sleepy)
272         (void)sleep(10);
273
274     /* Open the output file if the user requested it.  If the user
275      * requested '-R', we use the 'b' flag in fopen() to "do the right
276      * thing" in non-linux/unix OSes. */
277     if (outfile == NULL) {
278         fp = stdout;
279     } else {
280         if (binary)
281             fp = fopen(outfile, "wb");
282         else
283             fp = fopen(outfile, "w");
284
285         if (fp == NULL) {
286             (void)fprintf(stderr,
287                           "gpspipe: unable to open output file:  %s\n",
288                           outfile);
289             exit(1);
290         }
291     }
292
293     /* Open the serial port and set it up. */
294     if (serialport)
295         open_serial(serialport);
296
297     /*@ -nullpass -onlytrans @*/
298     if (gps_open_r(source.server, source.port, &gpsdata) != 0) {
299         (void)fprintf(stderr,
300                       "gpspipe: could not connect to gpsd %s:%s, %s(%d)\n",
301                       source.server, source.port, strerror(errno), errno);
302         exit(1);
303     }
304     /*@ +nullpass +onlytrans @*/
305
306     if (source.device != NULL)
307         flags |= WATCH_DEVICE;
308     (void)gps_stream(&gpsdata, flags, source.device);
309
310     if ((isatty(STDERR_FILENO) == 0) || daemon)
311         vflag = 0;
312
313     for (;;) {
314         int i = 0;
315         int j = 0;
316         int readbytes = 0;
317
318         if (vflag)
319             spinner(vflag, l++);
320
321         /* reading directly from the socket avoids decode overhead */
322         readbytes = (int)read(gpsdata.gps_fd, buf, sizeof(buf));
323         if (readbytes > 0) {
324             for (i = 0; i < readbytes; i++) {
325                 char c = buf[i];
326                 if (j < (int)(sizeof(serbuf) - 1)) {
327                     serbuf[j++] = buf[i];
328                 }
329                 if (new_line && timestamp) {
330                     time_t now = time(NULL);
331
332                     struct tm *tmp_now = localtime(&now);
333                     (void)strftime(tmstr, sizeof(tmstr), format, tmp_now);
334                     new_line = 0;
335                     if (fprintf(fp, "%.24s :", tmstr) <= 0) {
336                         (void)fprintf(stderr,
337                                       "gpspipe: write error, %s(%d)\n",
338                                       strerror(errno), errno);
339                         exit(1);
340                     }
341                 }
342                 if (fputc(c, fp) == EOF) {
343                     fprintf(stderr, "gpspipe: Write Error, %s(%d)\n",
344                             strerror(errno), errno);
345                     exit(1);
346                 }
347
348                 if (c == '\n') {
349                     if (serialport != NULL) {
350                         if (write(fd_out, serbuf, (size_t) j) == -1) {
351                             fprintf(stderr,
352                                     "gpspipe: Serial port write Error, %s(%d)\n",
353                                     strerror(errno), errno);
354                             exit(1);
355                         }
356                         j = 0;
357                     }
358
359                     new_line = true;
360                     /* flush after every good line */
361                     if (fflush(fp)) {
362                         (void)fprintf(stderr,
363                                       "gpspipe: fflush Error, %s(%d)\n",
364                                       strerror(errno), errno);
365                         exit(1);
366                     }
367                     if (count > 0) {
368                         if (0 >= --count) {
369                             /* completed count */
370                             exit(0);
371                         }
372                     }
373                 }
374             }
375         } else {
376             if (readbytes == -1) {
377                 (void)fprintf(stderr, "gpspipe: read error %s(%d)\n",
378                               strerror(errno), errno);
379                 exit(1);
380             } else {
381                 exit(0);
382             }
383         }
384     }
385
386 #ifdef __UNUSED__
387     if (serialport != NULL) {
388         /* Restore the old serial port settings. */
389         if (tcsetattr(fd_out, TCSANOW, &oldtio) != 0) {
390             (void)fprintf(stderr, "Error restoring serial port settings\n");
391             exit(1);
392         }
393     }
394 #endif /* __UNUSED__ */
395
396     /*@i1@*/ exit(0);
397 }
398
399 /*@ +compdestroy @*/
400
401 static void spinner(unsigned int v, unsigned int num)
402 {
403     char *spin = "|/-\\";
404
405     (void)fprintf(stderr, "\010%c", spin[(num / (1 << (v - 1))) % 4]);
406     (void)fflush(stderr);
407     return;
408 }