diff options
author | Dan McGee <dpmcgee@gmail.com> | 2008-09-16 22:20:17 -0500 |
---|---|---|
committer | Dan McGee <dan@archlinux.org> | 2008-10-05 19:49:38 -0500 |
commit | cf7ca31c0d2895803a822ecb41fc394c25f05027 (patch) | |
tree | 3ff0ff028759974449f7f801857304b9d805decf | |
parent | 8b267356438f7b428d150f34f011e7cdb351c30b (diff) | |
download | onkyocontrol-cf7ca31c0d2895803a822ecb41fc394c25f05027.tar.gz onkyocontrol-cf7ca31c0d2895803a822ecb41fc394c25f05027.zip |
More fun changes
* Play around with strdup() since it isn't actually part of ISO C.
* Get code to compile under c89, c99, and gnu99
* Rework open_listener() to use getaddrinfo()
Signed-off-by: Dan McGee <dpmcgee@gmail.com>
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | onkyo.c | 96 | ||||
-rw-r--r-- | onkyo.h | 43 | ||||
-rw-r--r-- | receiver.c | 9 | ||||
-rw-r--r-- | util.c | 66 |
5 files changed, 150 insertions, 73 deletions
@@ -1,9 +1,10 @@ # Makefile for Onkyo Receiver communication program -CFLAGS = -Wall -Wextra -g -O2 -march=i686 -#CFLAGS = -Wall -Wextra -g -pedantic -fstrict-aliasing -Wunreachable-code -fstack-protector -march=native +#CFLAGS = -Wall -Wextra -ggdb3 -O2 -march=i686 +# -Wunreachable-code -pedantic -march=native -fstack-protector -std=c99 +CFLAGS = -Wall -Wextra -ggdb -O2 -fstrict-aliasing -march=native program = onkyocontrol -objects = command.o onkyo.o receiver.o +objects = command.o onkyo.o receiver.o util.o .PHONY: all clean @@ -24,6 +25,8 @@ receiver.o: Makefile receiver.c onkyo.h onkyo.o: Makefile onkyo.c onkyo.h +util.o: Makefile util.c onkyo.h + doc: mkdir doc doxygen @@ -20,6 +20,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#define _POSIX_C_SOURCE 1 /* signal handlers, getaddrinfo */ +#define _XOPEN_SOURCE 500 /* SA_RESTART */ + #include <stdlib.h> #include <stdio.h> #include <signal.h> @@ -68,7 +71,7 @@ static void cleanup(int ret) __attribute__ ((noreturn)); static void pipehandler(int signo); static void realhandler(int signo); static int open_serial_device(const char *path); -static int open_listener(const char *host, int port); +static int open_listener(const char *host, const char *service); static int open_connection(int fd); static void end_connection(conn *c); static int process_input(conn *c); @@ -238,14 +241,14 @@ static int open_serial_device(const char *path) * Open a listening socket on the given bind address and port number. * Also add it to our global list of listeners. * @param host the hostname to bind to; NULL or "any" for all addresses - * @param port the port number to listen on + * @param service the service name or port number to bind to * @return the new socket fd */ -static int open_listener(const char *host, int port) +static int open_listener(const char *host, const char *service) { - int i, fd; - struct sockaddr_in sin; - const int trueval = 1; + int i, ret, fd = -1; + struct addrinfo hints; + struct addrinfo *result, *rp; /* make sure we have room in our list */ for(i = 0; i < MAX_LISTENERS && listeners[i] > -1; i++) @@ -255,48 +258,65 @@ static int open_listener(const char *host, int port) return(-1); } - /* open an inet socket with the default protocol */ - fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd < 0) { - perror(host); - return(-1); - } - /* set up our connection host, port, etc. */ - memset(&sin, 0, sizeof(struct sockaddr_in)); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - /* determine our resolved host address */ + /* set up our hints structure with known info */ + memset(&hints, 0, sizeof(struct addrinfo)); + /* allow IPv4 or IPv6 */ + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + /* passive flag is ignored if a node is specified in getaddrinfo call */ + hints.ai_flags = AI_PASSIVE; + hints.ai_protocol = 0; + + /* decide whether to pass a host into our getaddrinfo call. */ if(!host || strcmp(host, "any") == 0) { - sin.sin_addr.s_addr = INADDR_ANY; + ret = getaddrinfo(NULL, service, &hints, &result); } else { - struct hostent *he; - if(!(he = gethostbyname(host))) { - perror(host); - return(-1); - } - /* assumption- using IPv4, he->h_addrtype = AF_INET */ - memcpy((char *)&sin.sin_addr.s_addr, - (char *)he->h_addr, he->h_length); + ret = getaddrinfo(host, service, &hints, &result); } - /* set the ability to reuse local addresses */ - if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&trueval, - sizeof(int)) < 0) { - perror("setsockopt()"); + if(ret != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); return(-1); } - /* bind to the given address */ - if(bind(fd, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) { - perror("bind()"); - return(-1); + + /* we get a list of results back, try to bind to each until one works */ + for(rp = result; rp != NULL; rp = rp->ai_next) { + int on = 1; + /* attempt to open the socket */ + fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (fd == -1) + continue; + + /* attempt to set the ability to reuse local addresses */ + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + /* attempt bind to the given address */ + if(bind(fd, result->ai_addr, rp->ai_addrlen) == 0) + /* we found one that works! */ + break; + + /* oh noes, didn't work, keep looping */ + close(fd); + } + + /* if we didn't find an available socket/bind, we are out of luck */ + if(rp == NULL) { + fprintf(stderr, "could not bind to any available addresses\n"); + fd = -1; } + + /* make sure we free the result of our addr query */ + freeaddrinfo(result); + /* start listening */ - if(listen(fd, 5) < 0) { + if(fd != -1 && listen(fd, 5) < 0) { perror("listen()"); - return(-1); + fd = -1; } /* place the listener in our array */ - listeners[i] = fd; + if(fd != -1) { + listeners[i] = fd; + } return(fd); } @@ -499,7 +519,7 @@ int main(int argc, char *argv[]) init_commands(); /* open our listener connection */ - open_listener(NULL, 8701); + open_listener(LISTENHOST, LISTENPORT); /* Terminal settings are all done. Now it is time to watch for input * on our socket and handle it as necessary. We also handle incoming @@ -7,12 +7,16 @@ #ifndef ONKYO_H #define ONKYO_H -#include <unistd.h> /* close, read, write */ -#include <errno.h> /* for errno refs */ +#include <sys/types.h> /* ssize_t, size_t */ /** The serial port device to connect on */ #define SERIALDEVICE "/dev/ttyS0" +/** The hostname to listen on; "any" will listen on any interface */ +#define LISTENHOST "any" +/** The port number to listen on (note: it is a string, not a num) */ +#define LISTENPORT "8701" + /** Max size for our serial device pool */ #define MAX_SERIALDEVS 1 /** Max size for our listener pool */ @@ -44,33 +48,14 @@ void free_commands(void); char *process_incoming_message(int serialfd); int process_command(int serialfd, const char *str); -/* trivial functions, keep them inlined */ -static inline void xclose(int fd) -{ - while(close(fd) && errno == EINTR); -} - -static inline ssize_t xread(int fd, void *buf, size_t len) -{ - ssize_t nr; - while(1) { - nr = read(fd, buf, len); - if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) - continue; - return nr; - } -} - -static inline ssize_t xwrite(int fd, const void *buf, size_t len) -{ - ssize_t nr; - while(1) { - nr = write(fd, buf, len); - if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) - continue; - return nr; - } -} +/* util.c - trivial utility functions */ +void xclose(int fd); +ssize_t xread(int fd, void *buf, size_t len); +ssize_t xwrite(int fd, const void *buf, size_t len); +/* if using ISO C, strdup() is not actually defined, provide our own */ +#ifndef strdup +char *strdup(const char *s); +#endif /* strdup */ #endif /* ONKYO_H */ @@ -20,7 +20,6 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <errno.h> #include <string.h> #include "onkyo.h" @@ -69,8 +68,12 @@ int rcvr_handle_status(int serialfd, char **status) if(retval > 0) { buf[retval] = '\0'; /* return the status message if asked for */ - if(status) - *status = strdup(buf); + if(status) { + const size_t len = strlen(buf) + 1; + *status = malloc(len * sizeof(char)); + if(*status) + memcpy(*status, buf, len); + } return(0); } @@ -0,0 +1,66 @@ +/* + * util.c - Onkyo receiver utility functions + * + * Copyright (c) 2008 Dan McGee <dpmcgee@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> /* malloc */ +#include <unistd.h> /* close, read, write */ +#include <errno.h> /* for errno refs */ +#include <string.h> /* memcpy */ + +void xclose(int fd) +{ + while(close(fd) && errno == EINTR); +} + +ssize_t xread(int fd, void *buf, size_t len) +{ + ssize_t nr; + while(1) { + nr = read(fd, buf, len); + if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) + continue; + return nr; + } +} + +ssize_t xwrite(int fd, const void *buf, size_t len) +{ + ssize_t nr; + while(1) { + nr = write(fd, buf, len); + if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) + continue; + return nr; + } +} + +/* if using ISO C, strdup() is not actually defined, provide our own */ +#ifndef strdup +char *strdup(const char *s) +{ + char *ret = NULL; + if(s) { + const size_t len = strlen(s) + 1; + ret = malloc(len * sizeof(char)); + if(ret) + memcpy(ret, s, len); + } + return(ret); +} +#endif /* strdup */ + |