nagios4/lib/nsock.c

154 lines
3.3 KiB
C

#define _GNU_SOURCE 1
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdarg.h>
#include <fcntl.h>
#include "snprintf.h"
#include "lnag-utils.h"
#include "nsock.h"
#include <limits.h>
const char *nsock_strerror(int code)
{
switch (code) {
case NSOCK_EBIND: return "bind() failed";
case NSOCK_ELISTEN: return "listen() failed";
case NSOCK_ESOCKET: return "socket() failed";
case NSOCK_EUNLINK: return "unlink() failed";
case NSOCK_ECONNECT: return "connect() failed";
case NSOCK_EFCNTL: return "fcntl() failed";
case NSOCK_EINVAL: return "Invalid arguments";
}
return "Unknown error";
}
int nsock_unix(const char *path, unsigned int flags)
{
static int listen_backlog = INT_MAX;
struct sockaddr_un saun;
struct sockaddr *sa;
int sock = 0, mode;
socklen_t slen;
if(!path)
return NSOCK_EINVAL;
if(flags & NSOCK_TCP)
mode = SOCK_STREAM;
else if(flags & NSOCK_UDP)
mode = SOCK_DGRAM;
else
return NSOCK_EINVAL;
if((sock = socket(AF_UNIX, mode, 0)) < 0) {
return NSOCK_ESOCKET;
}
/* set up the sockaddr_un struct and the socklen_t */
sa = (struct sockaddr *)&saun;
memset(&saun, 0, sizeof(saun));
saun.sun_family = AF_UNIX;
slen = strlen(path);
memcpy(&saun.sun_path, path, slen);
slen += offsetof(struct sockaddr_un, sun_path);
/* unlink if we're supposed to, but not if we're connecting */
if(flags & NSOCK_UNLINK && !(flags & NSOCK_CONNECT)) {
if(unlink(path) < 0 && errno != ENOENT)
return NSOCK_EUNLINK;
}
if(flags & NSOCK_CONNECT) {
if(connect(sock, sa, slen) < 0) {
close(sock);
return NSOCK_ECONNECT;
}
return sock;
} else {
if(bind(sock, sa, slen) < 0) {
close(sock);
return NSOCK_EBIND;
}
}
if(!(flags & NSOCK_BLOCK) && fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
return NSOCK_EFCNTL;
if(flags & NSOCK_UDP)
return sock;
/* Default the backlog number on listen() to INT_MAX. If INT_MAX fails,
* try using SOMAXCONN (usually 127) and if that fails, return an error */
for (;;) {
if (listen(sock, listen_backlog)) {
if (listen_backlog == SOMAXCONN) {
close(sock);
return NSOCK_ELISTEN;
} else
listen_backlog = SOMAXCONN;
}
break;
}
return sock;
}
static inline int nsock_vprintf(int sd, const char *fmt, va_list ap, int plus)
{
va_list ap_dup;
char stack_buf[4096];
int len;
va_copy(ap_dup, ap);
len = vsnprintf(stack_buf, sizeof(stack_buf), fmt, ap_dup);
va_end(ap_dup);
if (len < 0)
return len;
if (len < sizeof(stack_buf)) {
stack_buf[len] = 0;
if (plus) ++len; /* Include the nul byte if requested. */
return write(sd, stack_buf, len);
} else {
char *heap_buf = NULL;
len = vasprintf(&heap_buf, fmt, ap);
if (len < 0)
return len;
if (plus) ++len; /* Include the nul byte if requested. */
len = write(sd, heap_buf, len);
free(heap_buf);
return len;
}
}
int nsock_printf_nul(int sd, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = nsock_vprintf(sd, fmt, ap, 1);
va_end(ap);
return ret;
}
int nsock_printf(int sd, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = nsock_vprintf(sd, fmt, ap, 0);
va_end(ap);
return ret;
}