* Do not include system headers in shared files (stdlib.h). * Don't include lcd.h only once in each driver. * Remove some unnecessary headers from driver (e.g. syslog.h) * Include <poll.h> instead of <sys/poll.h> * Spelling fixes.
332 lines
7.1 KiB
C
332 lines
7.1 KiB
C
/** \file shared/sockets.c
|
|
* Socket functions available to server and clients.
|
|
*/
|
|
|
|
/*-
|
|
* This file is part of LCDproc.
|
|
*
|
|
* Feel free to use this in your own clients... :)
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
#include <stdarg.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "report.h"
|
|
#include "sockets.h"
|
|
|
|
// Length of longest transmission allowed at once...
|
|
#define MAXMSG 8192
|
|
|
|
typedef struct sockaddr_in sockaddr_in;
|
|
|
|
/**
|
|
* Tries to resolve a resolve a hostname.
|
|
* \param name Pointer to resolves IP-address
|
|
* \param hostname Hostname or IP-address (as string)
|
|
* \param port Port number
|
|
* \return 0 on success, -1 on error.
|
|
*/
|
|
static int
|
|
sock_init_sockaddr (sockaddr_in *name, const char *hostname, unsigned short int port)
|
|
{
|
|
struct hostent *hostinfo;
|
|
|
|
memset (name, '\0', sizeof (*name));
|
|
name->sin_family = AF_INET;
|
|
name->sin_port = htons (port);
|
|
hostinfo = gethostbyname (hostname);
|
|
if (hostinfo == NULL) {
|
|
report (RPT_ERR, "sock_init_sockaddr: Unknown host %s.", hostname);
|
|
return -1;
|
|
}
|
|
name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Connect to server.
|
|
* \param host Hostname or IP-address
|
|
* \param port Port number
|
|
* \return socket file descriptor on success, -1 on error
|
|
*/
|
|
int
|
|
sock_connect (char *host, unsigned short int port)
|
|
{
|
|
struct sockaddr_in servername;
|
|
int sock;
|
|
int err = 0;
|
|
|
|
report (RPT_DEBUG, "sock_connect: Creating socket");
|
|
sock = socket (PF_INET, SOCK_STREAM, 0);
|
|
if (sock < 0) {
|
|
report (RPT_ERR, "sock_connect: Error creating socket");
|
|
return sock;
|
|
}
|
|
debug (RPT_DEBUG, "sock_connect: Created socket (%i)", sock);
|
|
|
|
if (sock_init_sockaddr (&servername, host, port) < 0)
|
|
return -1;
|
|
|
|
err = connect (sock, (struct sockaddr *) &servername, sizeof (servername));
|
|
if (err < 0) {
|
|
report (RPT_ERR, "sock_connect: connect failed");
|
|
shutdown (sock, SHUT_RDWR);
|
|
return -1;
|
|
}
|
|
|
|
fcntl (sock, F_SETFL, O_NONBLOCK);
|
|
|
|
return sock;
|
|
}
|
|
|
|
/**
|
|
* Disconnect from server.
|
|
* \param fd Socket file descriptor
|
|
* \return 0 on success, -1 on error.
|
|
*/
|
|
int
|
|
sock_close (int fd)
|
|
{
|
|
int err;
|
|
|
|
err = shutdown (fd, SHUT_RDWR);
|
|
if (!err)
|
|
close (fd);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/**
|
|
* Send printf-like formatted output.
|
|
* \param fd Socket file descriptor
|
|
* \param format Format string
|
|
* \param ... Arguments to the format string
|
|
* \return Number of bytes sent.
|
|
*/
|
|
int
|
|
sock_printf(int fd, const char *format, .../*args*/ )
|
|
{
|
|
char buf[MAXMSG];
|
|
va_list ap;
|
|
int size = 0;
|
|
|
|
va_start(ap, format);
|
|
size = vsnprintf(buf, sizeof(buf), format, ap);
|
|
va_end(ap);
|
|
|
|
if (size < 0) {
|
|
report(RPT_ERR, "sock_printf: vsnprintf failed");
|
|
return -1;
|
|
}
|
|
if (size > sizeof(buf))
|
|
report(RPT_WARNING, "sock_printf: vsnprintf truncated message");
|
|
|
|
return sock_send_string(fd, buf);
|
|
}
|
|
|
|
/**
|
|
* Send lines of text.
|
|
* \param fd Socket file descriptor
|
|
* \param string Pointer to the string to send.
|
|
* \return Number of bytes sent.
|
|
*/
|
|
int
|
|
sock_send_string (int fd, char *string)
|
|
{
|
|
return sock_send(fd, string, strlen(string));
|
|
}
|
|
|
|
/**
|
|
* Receive a line of text.
|
|
* Recv gives only one line per call...
|
|
* \param fd Socket file descriptor
|
|
* \param dest Pointer to buffer to store the received data
|
|
* \param maxlen Number of bytes to read at most (size of buffer)
|
|
* \return Number of bytes received.
|
|
*/
|
|
int
|
|
sock_recv_string (int fd, char *dest, size_t maxlen)
|
|
{
|
|
char *ptr = dest;
|
|
int recvBytes = 0;
|
|
|
|
if (!dest)
|
|
return -1;
|
|
if (maxlen <= 0)
|
|
return 0;
|
|
|
|
while (1) {
|
|
int err = read (fd, ptr, 1);
|
|
if (err == -1) {
|
|
if (errno == EAGAIN) {
|
|
if (recvBytes) {
|
|
// We've begun to read a string, but no bytes are
|
|
// available. Loop.
|
|
continue;
|
|
}
|
|
return 0;
|
|
} else {
|
|
report (RPT_ERR, "sock_recv_string: socket read error");
|
|
return err;
|
|
}
|
|
} else if (err == 0) {
|
|
return recvBytes;
|
|
}
|
|
|
|
recvBytes++;
|
|
|
|
// stop at max. bytes allowed, at NUL or at LF
|
|
if (recvBytes == maxlen || *ptr == '\0' || *ptr == '\n') {
|
|
*ptr = '\0';
|
|
break;
|
|
}
|
|
ptr++;
|
|
}
|
|
|
|
// Don't return an empty string
|
|
if (recvBytes == 1 && dest[0] == '\0')
|
|
return 0;
|
|
|
|
if (recvBytes < maxlen - 1)
|
|
dest[recvBytes] = '\0';
|
|
|
|
return recvBytes;
|
|
}
|
|
|
|
/**
|
|
* Send raw data.
|
|
* \param fd Socket file descriptor
|
|
* \param src Buffer holding the data to send
|
|
* \param size Number of bytes to send at most
|
|
* \return Number of bytes sent.
|
|
*/
|
|
int
|
|
sock_send (int fd, void *src, size_t size)
|
|
{
|
|
int offset = 0;
|
|
|
|
if (!src)
|
|
return -1;
|
|
|
|
while (offset != size) {
|
|
// write isn't guaranteed to send the entire string at once,
|
|
// so we have to sent it in a loop like this
|
|
int sent = write (fd, ((char *) src) + offset, size - offset);
|
|
if (sent == -1) {
|
|
if (errno != EAGAIN) {
|
|
report (RPT_ERR, "sock_send: socket write error");
|
|
report (RPT_DEBUG, "Message was: '%.*s'", size-offset, (char *) src);
|
|
return sent;
|
|
}
|
|
continue;
|
|
} else if (sent == 0) {
|
|
// when this returns zero, it generally means
|
|
// we got disconnected
|
|
return sent + offset;
|
|
}
|
|
|
|
offset += sent;
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
/**
|
|
* Receive raw data.
|
|
* \param fd Socket file descriptor
|
|
* \param dest Pointer to buffer to store the received data
|
|
* \param maxlen Number of bytes to read at most (size of buffer)
|
|
* \return Number of bytes received.
|
|
*/
|
|
int
|
|
sock_recv (int fd, void *dest, size_t maxlen)
|
|
{
|
|
int err;
|
|
|
|
if (!dest)
|
|
return -1;
|
|
if (maxlen <= 0)
|
|
return 0;
|
|
|
|
err = read (fd, dest, maxlen);
|
|
if (err < 0) {
|
|
//report (RPT_DEBUG,"sock_recv: socket read error");
|
|
return err;
|
|
}
|
|
//debug(RPT_DEBUG, "sock_recv: Got message \"%s\"", (char *)dest);
|
|
|
|
return err;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* Return the error message for the last error occured.
|
|
* \return Error message string
|
|
*/
|
|
char*
|
|
sock_geterror(void)
|
|
{
|
|
return strerror(errno);
|
|
}
|
|
|
|
/**
|
|
* Send an already formatted error message to the client.
|
|
* \param fd socket
|
|
* \param message the message to send (without the "huh? ") */
|
|
int sock_send_error(int fd, char* message)
|
|
{
|
|
// simple: performance penalty isn't worth more work...
|
|
return sock_printf_error(fd, message);
|
|
}
|
|
|
|
/**
|
|
* Print printf-like formatted output to logfile and sends it to the client.
|
|
* \note don't add a the "huh? " to the message. This is done by this
|
|
* method
|
|
* \param fd socket
|
|
* \param format a printf format */
|
|
int
|
|
sock_printf_error(int fd, const char *format, .../*args*/ )
|
|
{
|
|
static const char huh[] = "huh? ";
|
|
char buf[MAXMSG];
|
|
va_list ap;
|
|
int size = 0;
|
|
|
|
strncpy(buf, huh, sizeof(huh)); // note: sizeof(huh) < MAXMSG
|
|
|
|
va_start(ap, format);
|
|
size = vsnprintf(buf + (sizeof(huh)-1), sizeof(buf) - (sizeof(huh)-1), format, ap);
|
|
buf[sizeof(buf)-1] = '\0';
|
|
va_end(ap);
|
|
|
|
if (size < 0) {
|
|
report(RPT_ERR, "sock_printf_error: vsnprintf failed");
|
|
return -1;
|
|
}
|
|
if (size >= sizeof(buf) - (sizeof(huh)-1))
|
|
report(RPT_WARNING, "sock_printf_error: vsnprintf truncated message");
|
|
|
|
report(RPT_ERR, "error: %s", buf);
|
|
return sock_send_string(fd, buf);
|
|
}
|