gpsd.c

来自「gpsd, a popular GPS daemon.」· C语言 代码 · 共 1,824 行 · 第 1/4 页

C
1,824
字号
    /*@ -observertrans @*/    if (!rtcm_service)	rtcm_service = getservbyname("rtcm", "tcp") ? "rtcm" : DEFAULT_RTCM_PORT;    /*@ +observertrans @*/    if ((nsock = passivesock(rtcm_service, "tcp", QLEN)) < 0) {	gpsd_report(LOG_ERROR,"RTCM104 socket create failed, netlib error %d\n",nsock);	exit(2);    }    gpsd_report(LOG_INF, "listening on port %s\n", rtcm_service);#endif /* RTCM104_SERVICE */#ifdef NTPSHM_ENABLE    if (getuid() == 0) {	(void)nice(-10);		/* for precise timekeeping increase priority */	(void)ntpshm_init(&context, nowait);    } else {	gpsd_report (2, "Unable to start ntpshm.  gpsd must run as root.\n");    }#endif /* NTPSHM_ENABLE */#ifdef DBUS_ENABLE    /* we need to connect to dbus as root */    if (initialize_dbus_connection()) {	/* the connection could not be started */	gpsd_report (2, "unable to connect to the DBUS system bus\n");    } else	gpsd_report (2, "successfully connected to the DBUS system bus\n");#endif /* DBUS_ENABLE */    if (getuid() == 0 && go_background) {	struct passwd *pw;	struct stat stb;	/* make default devices accessible even after we drop privileges */	for (i = optind; i < argc; i++)	    if (stat(argv[i], &stb) == 0)		(void)chmod(argv[i], stb.st_mode|S_IRGRP|S_IWGRP);	/*	 * Drop privileges.  Up to now we've been running as root.  Instead,	 * set the user ID to 'nobody' (or whatever the --enable-gpsd-user	 * is) and the group ID to the owning group of a prototypical TTY	 * device. This limits the scope of any compromises in the code.	 * It requires that all GPS devices have their group read/write	 * permissions set.	 */	if ((optind<argc&&stat(argv[optind], &stb)==0)||stat(PROTO_TTY,&stb)==0) {	    gpsd_report(LOG_PROG, "changing to group %d\n", stb.st_gid);	    if (setgid(stb.st_gid) != 0)		gpsd_report(LOG_ERROR, "setgid() failed, errno %s\n", strerror(errno));	}	pw = getpwnam( GPSD_USER );	if (pw)	    (void)seteuid(pw->pw_uid);    }    gpsd_report(LOG_INF, "running with effective group ID %d\n", getegid());    gpsd_report(LOG_INF, "running with effective user ID %d\n", geteuid());    for (sub = subscribers; sub < subscribers + MAXSUBSCRIBERS; sub++) {	gps_clear_fix(&sub->fixbuffer);	gps_clear_fix(&sub->oldfix);    }    /* daemon got termination or interrupt signal */    if ((st = setjmp(restartbuf)) > 0) {	/* try to undo all device configurations */	for (dfd = 0; dfd < MAXDEVICES; dfd++) {	    if (allocated_channel(&channels[dfd]))		(void)gpsd_wrap(&channels[dfd]);	}	gpsd_report(LOG_WARN, "gpsd restarted by SIGHUP\n");    }    /* Handle some signals */    signalled = 0;    (void)signal(SIGHUP, onsig);    (void)signal(SIGINT, onsig);    (void)signal(SIGTERM, onsig);    (void)signal(SIGQUIT, onsig);    (void)signal(SIGPIPE, SIG_IGN);    FD_SET(msock, &all_fds);    adjust_max_fd(msock, true);#ifdef RTCM104_SERVICE    FD_SET(nsock, &all_fds);    adjust_max_fd(nsock, true);#endif /* RTCM104_SERVICE */    FD_ZERO(&control_fds);    /* optimization hack to defer having to read subframe data */    if (time(NULL) < START_SUBFRAME)	context.valid |= LEAP_SECOND_VALID;    for (i = optind; i < argc; i++) {	struct gps_device_t *device = open_device(argv[i]);	if (!device) {	    gpsd_report(LOG_ERROR, "GPS device %s nonexistent or can't be read\n", argv[i]);	}    }    while (0 == signalled) {	(void)memcpy((char *)&rfds, (char *)&all_fds, sizeof(rfds));	gpsd_report(LOG_RAW+2, "select waits\n");	/*	 * Poll for user commands or GPS data.  The timeout doesn't	 * actually matter here since select returns whenever one of	 * the file descriptors in the set goes ready.  The point	 * of tracking maxfd is to keep the set of descriptors that	 * select(2) has to poll here as small as possible (for	 * low-clock-rate SBCs and the like).	 */	/*@ -usedef @*/	tv.tv_sec = 1; tv.tv_usec = 0;	if (select(maxfd+1, &rfds, NULL, NULL, &tv) < 0) {	    if (errno == EINTR)		continue;	    gpsd_report(LOG_ERROR, "select: %s\n", strerror(errno));	    exit(2);	}	/*@ +usedef @*/#ifdef __UNUSED__	{	    char dbuf[BUFSIZ];	    dbuf[0] = '\0';	    for (sub = subscribers; sub < subscribers + MAXSUBSCRIBERS; sub++)		if (FD_ISSET(sub->fd, &all_fds))		    (void)snprintf(dbuf + strlen(dbuf),				   sizeof(dbuf)-strlen(dbuf),				   " %d", sub->fd);	    strlcat(dbuf, "} -> {", BUFSIZ);	    for (sub = subscribers; sub < subscribers + MAXSUBSCRIBERS; sub++)		if (FD_ISSET(sub->fd, &rfds))		    (void)snprintf(dbuf + strlen(dbuf),				   sizeof(dbuf)-strlen(dbuf),				   " %d", sub->fd);	    gpsd_report(LOG_RAW, "Polling descriptor set: {%s}\n", dbuf);	}#endif /* UNUSED */	/* always be open to new client connections */	if (FD_ISSET(msock, &rfds)) {	    socklen_t alen = (socklen_t)sizeof(fsin);	    char *c_ip;	    /*@i1@*/int ssock = accept(msock, (struct sockaddr *) &fsin, &alen);	    if (ssock < 0)		gpsd_report(LOG_ERROR, "accept: %s\n", strerror(errno));	    else {		struct subscriber_t *client = NULL;		int opts = fcntl(ssock, F_GETFL);		if (opts >= 0)		    (void)fcntl(ssock, F_SETFL, opts | O_NONBLOCK);		c_ip = sock2ip(ssock);		client = allocate_client();		if (client == NULL) {		    gpsd_report(LOG_ERROR, "Client %s connect on fd %d -"			"no subscriber slots available\n", c_ip, ssock);		    (void)close(ssock);		} else {		    FD_SET(ssock, &all_fds);		    adjust_max_fd(ssock, true);		    client->fd = ssock;		    client->active = timestamp();		    client->tied = false;		    client->requires = ANY;		    gpsd_report(LOG_INF, "client %s (%d) connect on fd %d\n",			c_ip, sub_index(client), ssock);		}	    }	    FD_CLR(msock, &rfds);	}#ifdef RTCM104_SERVICE	/* also to RTCM client connections */	if (FD_ISSET(nsock, &rfds)) {	    char *c_ip;	    socklen_t alen = (socklen_t)sizeof(fsin);	    /*@i1@*/int ssock = accept(nsock, (struct sockaddr *)&fsin, &alen);	    if (ssock < 0)		gpsd_report(LOG_ERROR, "accept: %s\n", strerror(errno));	    else {		struct subscriber_t *client = NULL;		int opts = fcntl(ssock, F_GETFL);		if (opts >= 0)		    (void)fcntl(ssock, F_SETFL, opts | O_NONBLOCK);		c_ip = sock2ip(ssock);		client = allocate_client();		if (client == NULL) {		    gpsd_report(LOG_ERROR, "Client %s connect on fd %d -"			"no subscriber slots available\n", c_ip, ssock);		    close(ssock);		} else {		    FD_SET(ssock, &all_fds);		    adjust_max_fd(ssock, true);		    client->active = true;		    client->tied = false;		    client->requires = RTCM104;		    client->fd = ssock;		    gpsd_report(LOG_INF, "client %s (%d) connect on fd %d\n",			c_ip, sub_index(client), ssock);		}	    }	    FD_CLR(nsock, &rfds);	}#endif /* RTCM104_SERVICE */	/* also be open to new control-socket connections */	if (csock > -1 && FD_ISSET(csock, &rfds)) {	    socklen_t alen = (socklen_t)sizeof(fsin);	    /*@i1@*/int ssock = accept(csock, (struct sockaddr *) &fsin, &alen);	    if (ssock < 0)		gpsd_report(LOG_ERROR, "accept: %s\n", strerror(errno));	    else {		gpsd_report(LOG_INF, "control socket connect on %d\n", ssock);		FD_SET(ssock, &all_fds);		FD_SET(ssock, &control_fds);		adjust_max_fd(ssock, true);	    }	    FD_CLR(csock, &rfds);	}	if (context.dsock >= 0 && FD_ISSET(context.dsock, &rfds)) {	    /* be ready for DGPS reports */	    if (dgnss_poll(&context) == -1){		FD_CLR(context.dsock, &all_fds);		FD_CLR(context.dsock, &rfds);		context.dsock = -1;	    }	}	/* read any commands that came in over control sockets */	for (cfd = 0; cfd < FD_SETSIZE; cfd++)	    if (FD_ISSET(cfd, &control_fds)) {		char buf[BUFSIZ];		while (read(cfd, buf, sizeof(buf)-1) > 0) {		    gpsd_report(LOG_IO, "<= control(%d): %s\n", cfd, buf);		    handle_control(cfd, buf);		}		(void)close(cfd);		FD_CLR(cfd, &all_fds);		FD_CLR(cfd, &control_fds);		adjust_max_fd(cfd, false);	    }	/* poll all active devices */	for (channel = channels; channel < channels + MAXDEVICES; channel++) {	    if (!allocated_channel(channel))		continue;	    /* pass the current RTCM correction to the GPS if new */	    if (channel->device_type)		rtcm_relay(channel);	    /* get data from the device */	    changed = 0;	    if (channel->gpsdata.gps_fd >= 0 && FD_ISSET(channel->gpsdata.gps_fd, &rfds))	    {		gpsd_report(LOG_RAW+1, "polling %d\n", channel->gpsdata.gps_fd);		changed = gpsd_poll(channel);		if (changed == ERROR_SET) {		    gpsd_report(LOG_WARN, "packet sniffer failed to sync up\n");		    FD_CLR(channel->gpsdata.gps_fd, &all_fds);		    adjust_max_fd(channel->gpsdata.gps_fd, false);		    gpsd_deactivate(channel);		} else if ((changed & ONLINE_SET) == 0) {		    gpsd_report(LOG_INF, "GPS is offline (%lf sec since data)\n",				timestamp() - channel->gpsdata.online);		    FD_CLR(channel->gpsdata.gps_fd, &all_fds);		    adjust_max_fd(channel->gpsdata.gps_fd, false);		    gpsd_deactivate(channel);		    notify_watchers(channel, "GPSD,X=0\r\n");		}		else {		    /* handle laggy response to a firmware version query*/		    if ((changed & DEVICEID_SET) != 0) {			char id[NMEA_MAX];			assert(channel->device_type != NULL);			(void)snprintf(id, sizeof(id), "GPSD,I=%s",				       channel->device_type->type_name);			if (channel->subtype[0] != '\0') {			    (void)strlcat(id, " ", sizeof(id));			    (void)strlcat(id,channel->subtype,sizeof(id));			}			(void)strlcat(id, "\r\n", sizeof(id));			notify_watchers(channel, id);		    }		    /* copy/merge channel data into subscriber fix buffers */		    for (sub = subscribers;			 sub < subscribers + MAXSUBSCRIBERS;			 sub++) {			if (sub->device == channel) {			    if (sub->buffer_policy == casoc && (changed & CYCLE_START_SET)!=0)				gps_clear_fix(&sub->fixbuffer);			    /* don't downgrade mode if holding previous fix */			    if (sub->fixbuffer.mode > sub->device->gpsdata.fix.mode)				changed &=~ MODE_SET;			    gpsd_report(LOG_PROG,					"transfer mask on %s: %02x\n", sub->device->gpsdata.tag, changed);			    gps_merge_fix(&sub->fixbuffer,					  changed,					  &sub->device->gpsdata.fix);			    gpsd_error_model(sub->device,					     &sub->fixbuffer, &sub->oldfix);			}		    }		}#ifdef RTCM104_SERVICE		/* copy each RTCM-104 correction to all GPSes */		if ((changed & RTCM_SET) != 0) {		    for (gps = channels; gps < channels + MAXDEVICES; gps++)			if (gps->device_type != NULL && gps->device_type->rtcm_writer != NULL)			    (void)gps->device_type->rtcm_writer(gps, (char *)channel->outbuffer, channel->outbuflen);		}#endif /* RTCM104_SERVICE */	    }	    for (sub = subscribers; sub < subscribers + MAXSUBSCRIBERS; sub++) {		/* some listeners may be in watcher mode */		if (sub->watcher) {		    char cmds[4] = "";		    channel->poll_times[sub - subscribers] = timestamp();		    if (changed &~ ONLINE_SET) {			if (changed & (LATLON_SET | MODE_SET))			    (void)strlcat(cmds, "o", 4);			if (changed & SATELLITE_SET)			    (void)strlcat(cmds, "y", 4);			if (channel->gpsdata.profiling!=0)			    (void)strlcat(cmds, "$", 4);		    }		    if (cmds[0] != '\0')			(void)handle_gpsd_request(sub, cmds, (int)strlen(cmds));		}	    }#ifdef DBUS_ENABLE	    if (changed &~ ONLINE_SET) {		    if (changed & (LATLON_SET | MODE_SET))			    send_dbus_fix (channel);	    }#endif	}#ifdef NOT_FIXED	if (context.fixcnt > 0 && context.dsock == -1) {	    for (channel=channels; channel < channels+MAXDEVICES; channel++) {		if (channel->gpsdata.fix.mode > MODE_NO_FIX) {		    dgnss_autoconnect(&context,				      channel->gpsdata.fix.latitude,				      channel->gpsdata.fix.longitude);		    break;		}	    }	}#endif	/* accept and execute commands for all clients */	for (sub = subscribers; sub < subscribers + MAXSUBSCRIBERS; sub++) {	    if (sub->active == 0)		continue;	    if (FD_ISSET(sub->fd, &rfds)) {		char buf[BUFSIZ];		int buflen;		gpsd_report(LOG_PROG, "checking client(%d)\n", sub_index(sub));		if ((buflen = (int)read(sub->fd, buf, sizeof(buf) - 1)) <= 0) {		    detach_client(sub);		} else {		    if (buf[buflen-1] != '\n')			buf[buflen++] = '\n';		    buf[buflen] = '\0';		    gpsd_report(LOG_IO, "<= client(%d): %s", sub_index(sub), buf);#ifdef RTCM104_SERVICE		    if (sub->requires==RTCM104			|| sub->requires==ANY) {			if (handle_rtcm_request(sub, buf, buflen) < 0)			    detach_client(sub);		    } else#endif /* RTCM104_SERVICE */		    {			if (sub->device){			    /*			     * when a command comes in, to update .active to			     * timestamp() so we don't close the connection			     * after POLLER_TIMEOUT seconds. This makes			     * POLLER_TIMEOUT useful.			     */			    sub->active = sub->device->poll_times[sub_index(sub)] = timestamp();			}			if (handle_gpsd_request(sub, buf, buflen) < 0)			    detach_client(sub);		    }		}	    } else if (sub->device == NULL && timestamp() - sub->active > ASSIGNMENT_TIMEOUT) {		gpsd_report(LOG_WARN, "client(%d) timed out before assignment request.\n", sub_index(sub));		detach_client(sub);	    } else if (sub->device != NULL && !(sub->watcher || sub->raw>0) && timestamp() - sub->active > POLLER_TIMEOUT) {		gpsd_report(LOG_WARN, "client(%d) timed out on command wait.\n", cfd);		detach_client(sub);	    }	}	/*	 * Close devices with an identified packet type but no remaining	 * subscribers.  The reason the test has this particular form is	 * so that, immediately after device open, we'll keep reading	 * packets until a type is identified even though there are no	 * subscribers yet.  We need this to happen so that subscribers	 * can later choose a device by packet type.	 */	if (!nowait)	    for (channel=channels; channel < channels+MAXDEVICES; channel++) {		if (allocated_channel(channel)) {		    if (channel->packet.type != BAD_PACKET) {			bool need_gps = false;			for (cfd = 0; cfd < MAXSUBSCRIBERS; cfd++)			    if (subscribers[cfd].device == channel)				need_gps = true;			if (!need_gps && channel->gpsdata.gps_fd > -1) {			    gpsd_report(LOG_RAW, "unflagging descriptor %d\n", channel->gpsdata.gps_fd);			    FD_CLR(channel->gpsdata.gps_fd, &all_fds);			    adjust_max_fd(channel->gpsdata.gps_fd, false);			    gpsd_deactivate(channel);			}		    }		}	    }    }    /* if we make it here, we got a signal... deal with it */    /* restart on SIGHUP, clean up and exit otherwise */    if (SIGHUP == (int)signalled)	longjmp(restartbuf, 1);    gpsd_report(LOG_WARN, "Received terminating signal %d. Exiting...\n",signalled);    /* try to undo all device configurations */    for (dfd = 0; dfd < MAXDEVICES; dfd++) {	if (allocated_channel(&channels[dfd]))	    (void)gpsd_wrap(&channels[dfd]);    }    if (control_socket)	(void)unlink(control_socket);    if (pid_file)	(void)unlink(pid_file);    return 0;}/*@ +mustfreefresh @*/

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?