From 3412e4a2ecb1da4ab3063209cb621c17d5ec9180 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sun, 1 Feb 2009 07:13:27 +1100 Subject: [PATCH] Add support for control and access over Unix Domain sockets. Listen on /var/run/gsm-mux for connections. Over these connections access commands reset_modem get_power set_power {0,1} alloc_channel set_name exit connect "connect" provides the significant new functionality. Further IO on that socket goes directly to a channel to the GSM controller. That way we can talk to it without needing to use a pty - simply open as socket and use that. EOF from a socket appears as a zero-byte read, so make sure to check for that in pseudo_device_read. Writing to a closed socket produces SIGPIPE, so we definitely don't want to exit on that case. In general, SIGPIPE is quite uninteresting, so universally ignore it. Signed-off-by: Neil Brown --- src/gsm0710muxd.c | 216 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 2 deletions(-) diff --git a/src/gsm0710muxd.c b/src/gsm0710muxd.c index eaef3e6..70d41cf 100644 --- a/src/gsm0710muxd.c +++ b/src/gsm0710muxd.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -613,7 +614,7 @@ gboolean pseudo_device_read(GIOChannel *source, GIOCondition condition, gpointer LOG(LOG_DEBUG, "Leave"); return TRUE; } - if (len >= 0) + if (len > 0) { LOG(LOG_DEBUG, "Data from channel %d, %d bytes", channel->id, len); if (channel->remaining > 0) @@ -1862,6 +1863,216 @@ static gboolean watchdog(gpointer data) return 1; } +struct unixsock { + int listener; + guint source; + struct sock { + struct sock *next; + int fd; + char linebuf[128]; + char name[64]; + int linelen; + int passthru; + guint source; + GIOChannel* io_channel; + } *sock; +} unixsock; + +int unix_connect(struct sock *s) +{ + int i; + if (serial.state != MUX_STATE_MUXING) + return 0; + + for (i=1 ; i < GSM0710_MAX_CHANNELS; i++) + if (channellist[i].fd < 0) { + int write_retries; + + LOG(LOG_DEBUG, "Found channel %d fd %d on %s", i, channellist[i].fd, channellist[i].devicename); + channellist[i].origin = strdup(s->name); + channellist[i].fd = s->fd; + + channellist[i].ptsname = NULL; + + channellist[i].v24_signals = GSM0710_SIGNAL_DV | GSM0710_SIGNAL_RTR | GSM0710_SIGNAL_RTC | GSM0710_EA; + channellist[i].g_channel = s->io_channel; + g_io_channel_set_encoding(channellist[i].g_channel, NULL, NULL ); + g_io_channel_set_buffer_size( channellist[i].g_channel, PTY_GLIB_BUFFER_SIZE ); + channellist[i].g_source = s->source; + LOG(LOG_INFO, "Connecting unix socket to virtual channel %d for %s on %s", + channellist[i].id, channellist[i].origin, serial.devicename); + + for (write_retries=0; write_retriespassthru = i; + return 1; + } + return 0; +} + +void unix_cmd(struct sock *s) +{ + char *a; + char buf[100]; + + a = "ERROR\n"; + if (strcasecmp(s->linebuf, "get_power") == 0) { + int n; + n = c_get_power(s->name); + if (n) + a = "OK on\n"; + else + a = "OK off\n"; + } + if (strncasecmp(s->linebuf, "set_power ", 10) == 0) { + int n = atoi(s->linebuf + 10); + c_set_power(s->name, n ? TRUE : FALSE); + a = "OK\n"; + } + if (strcasecmp(s->linebuf, "reset_modem") == 0) { + c_reset_modem(s->name); + a = "OK\n"; + } + if (strcasecmp(s->linebuf, "alloc_channel") == 0) { + const char *tty; + if (c_alloc_channel(s->name, &tty)) { + snprintf(buf, 100, "OK %s\n", tty); + a = buf; + } + } + if (strncasecmp(s->linebuf, "set_name ", 9) == 0 ) { + strncpy(s->name, s->linebuf+9, sizeof(s->name)); + s->name[sizeof(s->name)-1] = 0; + a = "OK\n"; + } + if (strcasecmp(s->linebuf, "connect") == 0) { + if (unix_connect(s)) + a = "OK\n"; + } + if (strcasecmp(s->linebuf, "exit") == 0) { + g_main_loop_quit(main_loop); + a = "OK\n"; + } + write(s->fd, a, strlen(a)); + return; +} + +gboolean unix_read(GIOChannel *source, GIOCondition condition, gpointer data) +{ + struct sock *s = data; + int n; + if (s->passthru) { + int r; + r = pseudo_device_read(source, condition, + channellist + s->passthru); + if (r) + return r; + s->fd = -1; + g_io_channel_unref(s->io_channel); + return r; + } + + while (( n = read(s->fd, s->linebuf + s->linelen, 1) ) == 1) { + if (s->linebuf[s->linelen] == '\n') { + s->linebuf[s->linelen] = 0; + s->linelen = 0; + unix_cmd(s); + return TRUE; + } else if (s->linelen < sizeof(s->linebuf)-2) + s->linelen++; + } + if (n == 0) { + /* EOF */ + close(s->fd); + s->fd = -1; + g_source_remove(s->source); + g_io_channel_unref(s->io_channel); + return FALSE; + } + return TRUE; +} + +gboolean unix_accept(GIOChannel *source, GIOCondition condition, gpointer data) +{ + struct unixsock *u = data; + struct sock *s, **sp; + int fd; + + /* free any closed socks */ + sp = &u->sock; + while ( (*sp) ) { + if ( (*sp)->fd == -1) { + s = *sp; + *sp = s->next; + free(s); + } else + sp = & ( (*sp)->next ); + } + + fd = accept(u->listener, NULL, NULL); + if (fd < 0) + return TRUE; + + s = malloc(sizeof(*s)); + s->fd = fd; + s->next = u->sock; + u->sock = s; + s->linelen = 0; + s->passthru = 0; + strcpy(s->name, "socket"); + s->io_channel = g_io_channel_unix_new(fd); + s->source = g_io_add_watch(s->io_channel, G_IO_IN, unix_read, s); + return TRUE; +} + +void unix_listen(char *path) +{ + int fd; + struct sockaddr_un addr; + int um; + + unixsock.listener = -1; + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, path); + unlink(path); + um = umask(077); + if (bind(fd, &addr, sizeof(addr)) != 0) { + umask(um); + close(fd); + return; + } + umask(um); + listen(fd, 10); + fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL, 0)); + unixsock.source = g_io_add_watch(g_io_channel_unix_new(fd), G_IO_IN, unix_accept, &unixsock); + unixsock.listener = fd; +} + /** * shows how to use this program */ @@ -2002,7 +2213,7 @@ int main( umask(0); //signals treatment signal(SIGHUP, signal_treatment); - signal(SIGPIPE, signal_treatment); + signal(SIGPIPE, SIG_IGN); signal(SIGKILL, signal_treatment); signal(SIGINT, signal_treatment); signal(SIGUSR1, signal_treatment); @@ -2012,6 +2223,7 @@ int main( else openlog(argv[0], LOG_NDELAY | LOG_PID, LOG_LOCAL0); SYSCHECK(dbus_init()); + unix_listen("/var/run/gsm-mux"); //allocate memory for data structures if ((serial.in_buf = gsm0710_buffer_init()) == NULL || (serial.adv_frame_buf = (unsigned char*)malloc((cmux_N1 + 3) * 2 + 2)) == NULL) -- 2.39.5