10df3451a6
Fix a possible free() of an uninitialized struct member in the error path that's hit in an OOM situation. Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
237 lines
4.1 KiB
C
237 lines
4.1 KiB
C
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <list.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <stdlib.h>
|
|
#include <debug.h>
|
|
#include <simpleconfig.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "serial.h"
|
|
|
|
struct socket_list {
|
|
list_head();
|
|
char *domain_name;
|
|
char *socket_path;
|
|
int socket_fd;
|
|
};
|
|
|
|
static struct socket_list *socks = NULL;
|
|
static pthread_mutex_t sock_list_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
static int
|
|
connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout)
|
|
{
|
|
int ret, flags, err;
|
|
unsigned l;
|
|
fd_set rfds, wfds;
|
|
struct timeval tv;
|
|
|
|
/*
|
|
Set up non-blocking connect
|
|
*/
|
|
flags = fcntl(fd, F_GETFL, 0);
|
|
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
ret = connect(fd, dest, len);
|
|
|
|
if ((ret < 0) && (errno != EINPROGRESS))
|
|
return -1;
|
|
|
|
if (ret == 0)
|
|
goto done;
|
|
|
|
FD_ZERO(&rfds);
|
|
FD_SET(fd, &rfds);
|
|
FD_ZERO(&wfds);
|
|
FD_SET(fd, &wfds);
|
|
|
|
tv.tv_sec = timeout;
|
|
tv.tv_usec = 0;
|
|
|
|
if (select(fd + 1, &rfds, &wfds, NULL, &tv) == 0) {
|
|
errno = ETIMEDOUT;
|
|
return -1;
|
|
}
|
|
|
|
if (!FD_ISSET(fd, &rfds) && !FD_ISSET(fd, &wfds)) {
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
|
|
l = sizeof(err);
|
|
if (getsockopt(fd, SOL_SOCKET, SO_ERROR,
|
|
(void *)&err, &l) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (err != 0) {
|
|
errno = err;
|
|
return -1;
|
|
}
|
|
|
|
done:
|
|
fcntl(fd, F_SETFL, flags);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
domain_sock_setup(const char *domain, const char *socket_path)
|
|
{
|
|
struct sockaddr_un *sun = NULL;
|
|
struct socket_list *node = NULL;
|
|
socklen_t sun_len;
|
|
int sock = -1;
|
|
|
|
sun_len = sizeof(*sun) + strlen(socket_path) + 1;
|
|
sun = malloc(sun_len);
|
|
if (!sun)
|
|
return -1;
|
|
|
|
memset((char *)sun, 0, sun_len);
|
|
sun->sun_family = PF_LOCAL;
|
|
strncpy(sun->sun_path, socket_path, sun_len - sizeof(*sun));
|
|
|
|
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
|
|
if (sock < 0)
|
|
goto out_fail;
|
|
|
|
if (connect_nb(sock, (struct sockaddr *)sun, SUN_LEN(sun), 3) < 0)
|
|
goto out_fail;
|
|
|
|
free(sun);
|
|
sun = NULL;
|
|
|
|
node = calloc(1, sizeof(*node));
|
|
if (!node)
|
|
goto out_fail;
|
|
|
|
node->domain_name = strdup(domain);
|
|
if (!node->domain_name)
|
|
goto out_fail;
|
|
|
|
node->socket_path = strdup(socket_path);
|
|
if (!node->socket_path)
|
|
goto out_fail;
|
|
|
|
node->socket_fd = sock;
|
|
|
|
pthread_mutex_lock(&sock_list_mutex);
|
|
list_insert(&socks, node);
|
|
pthread_mutex_unlock(&sock_list_mutex);
|
|
|
|
dbg_printf(3, "Registered %s on %d\n", domain, sock);
|
|
return 0;
|
|
|
|
out_fail:
|
|
if (node) {
|
|
free(node->domain_name);
|
|
if (node->socket_path)
|
|
free(node->socket_path);
|
|
free(node);
|
|
}
|
|
free(sun);
|
|
if (sock >= 0)
|
|
close(sock);
|
|
return -1;
|
|
}
|
|
|
|
|
|
int
|
|
domain_sock_close(const char *domain)
|
|
{
|
|
struct socket_list *node = NULL;
|
|
struct socket_list *dead = NULL;
|
|
int x;
|
|
|
|
pthread_mutex_lock(&sock_list_mutex);
|
|
list_for(&socks, node, x) {
|
|
if (!strcasecmp(domain, node->domain_name)) {
|
|
list_remove(&socks, node);
|
|
dead = node;
|
|
break;
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&sock_list_mutex);
|
|
|
|
if (dead) {
|
|
dbg_printf(3, "Unregistered %s, fd%d\n",
|
|
dead->domain_name,
|
|
dead->socket_fd);
|
|
close(dead->socket_fd);
|
|
free(dead->domain_name);
|
|
free(dead->socket_path);
|
|
free(dead);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
domain_sock_fdset(fd_set *fds, int *max)
|
|
{
|
|
struct socket_list *node = NULL;
|
|
int x = 0, _max = -1;
|
|
|
|
pthread_mutex_lock(&sock_list_mutex);
|
|
list_for(&socks, node, x) {
|
|
FD_SET(node->socket_fd, fds);
|
|
if (node->socket_fd > _max)
|
|
_max = node->socket_fd;
|
|
}
|
|
pthread_mutex_unlock(&sock_list_mutex);
|
|
|
|
if (max)
|
|
*max = _max;
|
|
|
|
return x;
|
|
}
|
|
|
|
|
|
int
|
|
domain_sock_name(int fd, char *outbuf, size_t buflen)
|
|
{
|
|
struct socket_list *node = NULL;
|
|
int ret = 1, x = 0;
|
|
|
|
pthread_mutex_lock(&sock_list_mutex);
|
|
list_for(&socks, node, x) {
|
|
if (node->socket_fd == fd) {
|
|
snprintf(outbuf, buflen, "%s", node->domain_name);
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&sock_list_mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int
|
|
domain_sock_cleanup(void)
|
|
{
|
|
struct socket_list *dead= NULL;
|
|
|
|
pthread_mutex_lock(&sock_list_mutex);
|
|
while(socks) {
|
|
dead = socks;
|
|
list_remove(&socks, dead);
|
|
close(dead->socket_fd);
|
|
free(dead->domain_name);
|
|
free(dead->socket_path);
|
|
free(dead);
|
|
}
|
|
pthread_mutex_unlock(&sock_list_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|